| 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 asoutto the advice, and assign it the value that Invoke returns. Make the advice generic, with a parameter of typeT, 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