| Severity | Warning |
| Reported by | PostSharp |
| Message | The advice "<Advice>" has both a MethodExecutionArgs parameter and the [ReturnValue] parameter "<Parameter>", passed as out or ref. The value of the [ReturnValue] parameter is used, and MethodExecutionArgs.ReturnValue is ignored. |
Cause
In a method boundary advice, a [ReturnValueAttribute] parameter passed as out or ref is the variable that holds the return value of the method: the value that the advice writes to it is the value that the method returns. When the advice also has a MethodExecutionArgs parameter, the woven code does not copy ReturnValue over this variable, so a value that the advice assigns to ReturnValue has no effect.
PostSharp reports LA0243 for the <Parameter> parameter when the advice <Advice> is an OnMethodEntryAdvice, OnMethodSuccessAdvice, OnMethodExceptionAdvice or OnMethodExitAdvice with both parameters. The build continues, and the value of the parameter wins.
In the following example, the success advice rounds the price through ReturnValue, but its price parameter is passed by reference, so the method returns the price without rounding.
using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0243_Error;
[PSerializable]
public class RoundPriceAttribute : MethodLevelAspect
{
[OnMethodSuccessAdvice]
public static void OnSuccess(
MethodExecutionArgs args,
// The parameter is passed by reference, so the method returns its value, not args.ReturnValue.
[ReturnValue] ref decimal price )
{
Console.WriteLine( $"Rounding the result of {args.Method.Name}." );
args.ReturnValue = Math.Round( price, 2 );
}
}
public class PriceCalculator
{
[RoundPrice]
public decimal GetDiscountedPrice( decimal price, decimal discount ) => price * (1 - discount);
}
public static class Program
{
public static void Main()
{
Console.WriteLine( new PriceCalculator().GetDiscountedPrice( 19.99m, 0.15m ) );
}
}
warning LA0243: LA0243_Error.cs(15,35): The advice "Errors.LA0243_Error.RoundPriceAttribute.OnSuccess(PostSharp.Aspects.MethodExecutionArgs, System.Decimal@)" has both a MethodExecutionArgs parameter and the [ReturnValue] parameter "price", passed as out or ref. The value of the [ReturnValue] parameter is used, and MethodExecutionArgs.ReturnValue is ignored.
The program prints the value that is not rounded:
Rounding the result of GetDiscountedPrice.
16.9915
Resolution
Set the return value in one place:
- Assign the value to the
outorref[ReturnValueAttribute] parameter, and remove the MethodExecutionArgs parameter. Bound parameters such as [DeclarationAttribute] or [DeclarationNameAttribute] give the other values that the advice reads from MethodExecutionArgs. - Or pass the [ReturnValueAttribute] parameter by value, so that it only reads the return value, and assign the new value to ReturnValue.
In the following example, the price parameter is passed by value, and the method returns the rounded price.
using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0243_Fixed;
[PSerializable]
public class RoundPriceAttribute : MethodLevelAspect
{
[OnMethodSuccessAdvice]
public static void OnSuccess(
MethodExecutionArgs args,
// The parameter is passed by value, so the method returns args.ReturnValue.
[ReturnValue] decimal price )
{
Console.WriteLine( $"Rounding the result of {args.Method.Name}." );
args.ReturnValue = Math.Round( price, 2 );
}
}
public class PriceCalculator
{
[RoundPrice]
public decimal GetDiscountedPrice( decimal price, decimal discount ) => price * (1 - discount);
}
public static class Program
{
public static void Main()
{
Console.WriteLine( new PriceCalculator().GetDiscountedPrice( 19.99m, 0.15m ) );
}
}
Rounding the result of GetDiscountedPrice.
16.99
See Also
Reference
ReturnValueAttribute
MethodExecutionArgs
Other Resources