| Severity | Error |
| Reported by | PostSharp |
| Message | The parameter "<Parameter>" of the advice "<Advice>" is bound by [ImportMember] and passed as out or ref to the field "<Field>" of a struct, but "<TargetMethod>" is an async method or an iterator, which works on a copy of the struct. Pass the parameter by value or as in. |
Cause
An advice parameter annotated with ImportMemberAttribute and passed as ref or out receives a reference to a field of the target object, so that the advice can change the field. An async method or an iterator runs in a state machine, which holds a copy of this when the target type is a struct. A reference to a field of this copy would not change the struct of the caller, so PostSharp reports LA0262 when the advice is woven into the state machine.
The message concerns only the instance fields of a struct: a static field, or a field of a class, can be imported by reference in an async method or an iterator.
In the following example, the [CountCompletions] aspect increments a field of the DownloadQueue struct after its async method completes.
using System;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0262_Error;
[PSerializable]
public class CountCompletionsAttribute : MethodLevelAspect
{
[OnMethodSuccessAdvice]
public static void OnSuccess( [ImportMember( "completedDownloads" )] ref int completedDownloads )
{
completedDownloads++;
}
}
public struct DownloadQueue
{
private int completedDownloads;
public int CompletedDownloads => this.completedDownloads;
public void Reset() => this.completedDownloads = 0;
// An async method of a struct works on a copy of the struct, so the advice cannot reference its field.
[CountCompletions]
public async Task DownloadAsync( string url )
{
await Task.Yield();
Console.WriteLine( $"Downloaded {url}" );
}
}
public static class Program
{
public static async Task Main()
{
DownloadQueue queue = new DownloadQueue();
await queue.DownloadAsync( "https://example.com/a.zip" );
await queue.DownloadAsync( "https://example.com/b.zip" );
Console.WriteLine( $"Completed downloads: {queue.CompletedDownloads}" );
}
}
error LA0262: LA0262_Error.cs(29,23): The parameter "completedDownloads" of the advice "Errors.LA0262_Error.CountCompletionsAttribute.OnSuccess(System.Int32@)" is bound by [ImportMember] and passed as out or ref to the field "Errors.LA0262_Error.DownloadQueue.completedDownloads" of a struct, but "Errors.LA0262_Error.DownloadQueue.DownloadAsync(System.String)" is an async method or an iterator, which works on a copy of the struct. Pass the parameter by value or as in.
Resolution
Choose one of the following:
- Declare the target type as a class, when it needs to change its own state from an async method or an iterator.
- Pass the parameter by value or as
in, when the advice only reads the field. The advice then reads the field of the copy that the state machine holds. - Make the field static, when its value does not belong to one object.
In the following example, DownloadQueue is a class, so the advice increments the field of the object that the caller holds.
using System;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;
namespace Errors.LA0262_Fixed;
[PSerializable]
public class CountCompletionsAttribute : MethodLevelAspect
{
[OnMethodSuccessAdvice]
public static void OnSuccess( [ImportMember( "completedDownloads" )] ref int completedDownloads )
{
completedDownloads++;
}
}
// A class, unlike a struct, is not copied by its async methods, so the advice references the field of the object.
public class DownloadQueue
{
private int completedDownloads;
public int CompletedDownloads => this.completedDownloads;
public void Reset() => this.completedDownloads = 0;
[CountCompletions]
public async Task DownloadAsync( string url )
{
await Task.Yield();
Console.WriteLine( $"Downloaded {url}" );
}
}
public static class Program
{
public static async Task Main()
{
DownloadQueue queue = new DownloadQueue();
await queue.DownloadAsync( "https://example.com/a.zip" );
await queue.DownloadAsync( "https://example.com/b.zip" );
Console.WriteLine( $"Completed downloads: {queue.CompletedDownloads}" );
}
}
Downloaded https://example.com/a.zip
Downloaded https://example.com/b.zip
Completed downloads: 2
See Also
Reference
Other Resources
Advice Parameters
Semantic Advising of Iterator and Async Methods