| Severity | Error |
| Reported by | PostSharp |
| Message | The parameter "<Parameter>" of the advice "<Advice>" is bound by [ImportMember] to the readonly method "<Method>" of a struct, but the first parameter of the delegate type "<DelegateType>" passes the struct as ref. A readonly method does not change the struct: declare the first parameter of the delegate as in. |
Cause
An advice parameter of a delegate type, marked with ImportMemberAttribute and a method name, receives an open delegate of a method of the target type. For an instance method of a struct, the first parameter of the delegate receives the struct by reference, so you declare a delegate type of your own. A readonly method receives the struct as in, because it does not change it, so the first parameter of the delegate must be in. Only a method that is not readonly accepts ref.
When no method of the name converts to the delegate type, and one of them would convert if the first parameter of the delegate were in instead of ref, PostSharp reports LA0277 on the target method, whatever the value of IsRequired.
In the following example, the delegate type LevelReader passes the Tank struct as ref to the readonly method ReadLevel.
using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0277_Error;
// ReadLevel is readonly, so the delegate cannot pass the struct as ref.
public delegate double LevelReader( ref Tank tank );
[PSerializable]
public class ReportLevelAttribute : MethodLevelAspect
{
[OnMethodSuccessAdvice]
public static void OnSuccess( [This] ref Tank tank, [ImportMember( "ReadLevel" )] LevelReader readLevel )
{
Console.WriteLine( $"Level: {readLevel( ref tank )}" );
}
}
public struct Tank
{
private double level;
public readonly double ReadLevel() => this.level;
[ReportLevel]
public void Fill( double amount ) => this.level += amount;
}
public static class Program
{
public static void Main()
{
var tank = new Tank();
tank.Fill( 10 );
tank.Fill( 5.5 );
}
}
error LA0277: LA0277_Error.cs(28,17): The parameter "readLevel" of the advice "Errors.LA0277_Error.ReportLevelAttribute.OnSuccess(Errors.LA0277_Error.Tank@, Errors.LA0277_Error.LevelReader)" is bound by [ImportMember] to the readonly method "Errors.LA0277_Error.Tank.ReadLevel" of a struct, but the first parameter of the delegate type "Errors.LA0277_Error.LevelReader" passes the struct as ref. A readonly method does not change the struct: declare the first parameter of the delegate as in.
Resolution
Declare the first parameter of the delegate type as in, and pass the struct with in when the advice calls the delegate. If the method must change the struct, remove its readonly modifier instead.
In the following example, LevelReader passes the struct as in.
using System;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0277_Fixed;
// The delegate passes the struct as in, which the readonly method ReadLevel accepts.
public delegate double LevelReader( in Tank tank );
[PSerializable]
public class ReportLevelAttribute : MethodLevelAspect
{
[OnMethodSuccessAdvice]
public static void OnSuccess( [This] ref Tank tank, [ImportMember( "ReadLevel" )] LevelReader readLevel )
{
Console.WriteLine( $"Level: {readLevel( in tank )}" );
}
}
public struct Tank
{
private double level;
public readonly double ReadLevel() => this.level;
[ReportLevel]
public void Fill( double amount ) => this.level += amount;
}
public static class Program
{
public static void Main()
{
var tank = new Tank();
tank.Fill( 10 );
tank.Fill( 5.5 );
}
}
Level: 10
Level: 15.5
See Also
Reference
Other Resources