Open sandboxFocusImprove this doc

LA0243: Return Value Set Both by Reference and on MethodExecutionArgs

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:

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

Advice Parameters