Open sandboxFocusImprove this doc

LA0221: Interception Advice Cannot Set the Return Value

Severity Error
Reported by PostSharp
Message The advice "<Advice>" cannot be applied to "<Target>" because the method returns a value, and the advice has neither a MethodInterceptionArgs parameter nor a [ReturnValue] parameter to set it.

Cause

An OnMethodInvokeAdvice replaces the body of the target method, so the value that the method returns is the value that the advice sets. The advice sets it through a parameter annotated with ReturnValueAttribute, passed as out or ref, or through the ReturnValue property of a MethodInterceptionArgs parameter. When the target returns a value and the advice has neither of them, PostSharp reports LA0221 on the target, because the method would always return the default value. A target that returns void does not need either.

In the following example, the [Retry] advice calls the intercepted method through its IMethodBinding, but discards the value it returns, and the aspect is applied to a method that returns a string.

using System;
using System.IO;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0221_Error;

[PSerializable]
public class RetryAttribute : MethodLevelAspect
{
    // The advice discards the value that the intercepted method returns.
    [OnMethodInvokeAdvice]
    public static void OnInvoke( [This] object? instance, [Binding] IMethodBinding binding, [Arguments] Arguments arguments )
    {
        for ( int attempt = 1; ; attempt++ )
        {
            try
            {
                binding.Invoke( ref instance, arguments );

                return;
            }
            catch ( IOException ) when ( attempt < 3 )
            {
                Console.WriteLine( $"Attempt {attempt} failed." );
            }
        }
    }
}

public class FileStore
{
    private int calls;

    [Retry]
    public string Read( string path )
    {
        this.calls++;

        if ( this.calls < 3 )
        {
            throw new IOException( "The file is locked." );
        }

        return $"Content of {path}.";
    }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( new FileStore().Read( "greeting.txt" ) );
    }
}
error LA0221: LA0221_Error.cs(37,19): The advice "Errors.LA0221_Error.RetryAttribute.OnInvoke(System.Object, PostSharp.Aspects.IMethodBinding, PostSharp.Aspects.Arguments)" cannot be applied to "Errors.LA0221_Error.FileStore.Read(System.String)" because the method returns a value, and the advice has neither a MethodInterceptionArgs parameter nor a [ReturnValue] parameter to set it.

Resolution

  • Add a [ReturnValue] parameter passed as out to the advice, and assign it the value that Invoke returns. Make the advice generic, with a parameter of type T, to fit any return type.
  • Apply the aspect only to methods that return void.

In the following example, the advice is generic, and sets the return value through a [ReturnValue] parameter.

using System;
using System.IO;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0221_Fixed;

[PSerializable]
public class RetryAttribute : MethodLevelAspect
{
    // The advice sets the return value of the method through a [ReturnValue] parameter.
    [OnMethodInvokeAdvice]
    public static void OnInvoke<T>( [This] object? instance, [Binding] IMethodBinding binding, [Arguments] Arguments arguments,
                                    [ReturnValue] out T returnValue )
    {
        for ( int attempt = 1; ; attempt++ )
        {
            try
            {
                returnValue = (T) binding.Invoke( ref instance, arguments );

                return;
            }
            catch ( IOException ) when ( attempt < 3 )
            {
                Console.WriteLine( $"Attempt {attempt} failed." );
            }
        }
    }
}

public class FileStore
{
    private int calls;

    [Retry]
    public string Read( string path )
    {
        this.calls++;

        if ( this.calls < 3 )
        {
            throw new IOException( "The file is locked." );
        }

        return $"Content of {path}.";
    }
}

public static class Program
{
    public static void Main()
    {
        Console.WriteLine( new FileStore().Read( "greeting.txt" ) );
    }
}
Attempt 1 failed.
Attempt 2 failed.
Content of greeting.txt.

See Also

Reference

OnMethodInvokeAdvice
ReturnValueAttribute
IMethodBinding

Other Resources

Advice Parameters