Open sandboxFocusImprove this doc

LA0112: Instance-Scoped Aspect Applied to a Value Type

Severity Error
Reported by PostSharp
Message Cannot apply the aspect "<Aspect>" to "<Target>": instance-scoped aspects cannot be applied to value types or members of value types.

Cause

An instance-scoped aspect, which implements IInstanceScopedAspect or derives from InstanceLevelAspect, has one instance per instance of its target type. PostSharp stores this instance in a field that it adds to the target type. PostSharp supports this for classes only, and reports LA0112 when the declaring type of the target is a value type: a struct, or a member of a struct.

In the following example, the instance-scoped [CountCalls] aspect is applied to a method of the Money struct.

using System;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0112_Error;

[PSerializable]
public class CountCallsAttribute : OnMethodBoundaryAspect, IInstanceScopedAspect
{
    private int count;

    public object CreateInstance( AdviceArgs adviceArgs ) => this.MemberwiseClone();

    public void RuntimeInitializeInstance()
    {
    }

    public override void OnEntry( MethodExecutionArgs args )
    {
        this.count++;
        Console.WriteLine( $"{args.Method.Name} called {this.count} time(s) on this instance." );
    }
}

public struct Money
{
    public decimal Amount { get; set; }

    // The instance-scoped aspect is applied to a member of a struct.
    [CountCalls]
    public string Format() => $"{this.Amount:0.00} EUR";
}

public static class Program
{
    public static void Main()
    {
        var price = new Money { Amount = 12.5m };
        Console.WriteLine( price.Format() );
        Console.WriteLine( price.Format() );
    }
}
error LA0112: LA0112_Error.cs(31,19): Cannot apply the aspect "Errors.LA0112_Error.CountCallsAttribute" to "Errors.LA0112_Error.Money.Format": instance-scoped aspects cannot be applied to value types or members of value types.

Resolution

  • Make the target type a class.
  • Or make the aspect static-scoped: remove IInstanceScopedAspect, so that one instance of the aspect serves all the instances of the target type.

In the following example, Money is a class, and each instance counts its own calls.

using System;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0112_Fixed;

[PSerializable]
public class CountCallsAttribute : OnMethodBoundaryAspect, IInstanceScopedAspect
{
    private int count;

    public object CreateInstance( AdviceArgs adviceArgs ) => this.MemberwiseClone();

    public void RuntimeInitializeInstance()
    {
    }

    public override void OnEntry( MethodExecutionArgs args )
    {
        this.count++;
        Console.WriteLine( $"{args.Method.Name} called {this.count} time(s) on this instance." );
    }
}

// Money is a class, so each instance can have its own instance of the aspect.
public class Money
{
    public decimal Amount { get; set; }

    [CountCalls]
    public string Format() => $"{this.Amount:0.00} EUR";
}

public static class Program
{
    public static void Main()
    {
        var price = new Money { Amount = 12.5m };
        Console.WriteLine( price.Format() );
        Console.WriteLine( price.Format() );
    }
}
Format called 1 time(s) on this instance.
12.50 EUR
Format called 2 time(s) on this instance.
12.50 EUR

See Also

Reference

IInstanceScopedAspect
InstanceLevelAspect

Other Resources

Understanding Aspect Lifetime and Scope