| Severity | Warning |
| Reported by | PostSharp |
| Message | The field "<Field>" is loaded by reference, which prevents aspects from intercepting this access. To call an instance method of the field, use the Post.GetValue method. To change a member of the field or pass the field by reference, copy it to a local variable, change or pass the local variable, and assign the local variable back to the field [in method "<Method>"]. |
Cause
An aspect that intercepts a field, such as a LocationInterceptionAspect, turns the field into a property: PostSharp replaces each read and each assignment of the field with a call of the getter or the setter, which run the aspect. A reference to the field cannot be replaced in this way. The C# compiler loads the address of a field instead of its value in the following cases:
- The code changes a member of a structure stored in the field, as in
this.location.X = x, or reads a member of it, as inthis.location.X. - The code calls an instance method of the structure stored in the field.
- The code passes the field to a
ref,outorinparameter.
PostSharp then leaves the access as it is and reports PS0089 on its line: the build succeeds, but the aspect does not see this access. PostSharp does not report the message when the code sets the field to its default value, or calls a method of a structure of the System namespace, such as DateTime. <Method> is the method that contains the access.
In the following example, the [Trace] aspect intercepts the assignments of the location field, but MoveRight changes the X field of the structure in place.
using System;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.PS0089_Error;
[PSerializable]
public class TraceAttribute : LocationInterceptionAspect
{
public override void OnSetValue( LocationInterceptionArgs args )
{
Console.WriteLine( $"Setting {args.LocationName} to {args.Value}." );
args.ProceedSetValue();
}
}
public struct Point
{
public Point( int x, int y )
{
this.X = x;
this.Y = y;
}
public int X;
public int Y;
public override string ToString() => $"({this.X}, {this.Y})";
}
public class Shape
{
[Trace]
private Point location;
public void MoveRight( int distance )
{
// Changes the structure in place, through a reference to the location field.
this.location.X += distance;
}
public string Describe() => $"Shape at {this.location}";
}
public static class Program
{
public static void Main()
{
Shape shape = new();
shape.MoveRight( 5 );
Console.WriteLine( shape.Describe() );
}
}
warning PS0089: PS0089_Error.cs(39,9): The field "Errors.PS0089_Error.Shape.location" is loaded by reference, which prevents aspects from intercepting this access. To call an instance method of the field, use the Post.GetValue method. To change a member of the field or pass the field by reference, copy it to a local variable, change or pass the local variable, and assign the local variable back to the field [in method "Errors.PS0089_Error.Shape.MoveRight(System.Int32)"].
The program runs, but the aspect does not trace the move:
Shape at (5, 0)
Resolution
- To change a member of the structure, copy the field to a local variable, change the local variable, and assign it back to the field. Better, make the structure immutable and assign a new value to the field.
- To call an instance method of the structure, pass the field to GetValue<T>(T), as in
Post.GetValue( this.location ).ToString(): the method is called on a copy of the value, which the getter returns. - To pass the field by reference, copy it to a local variable, pass the local variable, and assign it back to the field after the call.
In the following example, MoveRight changes a copy of the structure and assigns it to the field, which the aspect intercepts.
using System;
using PostSharp.Aspects;
using PostSharp.Serialization;
namespace Errors.PS0089_Fixed;
[PSerializable]
public class TraceAttribute : LocationInterceptionAspect
{
public override void OnSetValue( LocationInterceptionArgs args )
{
Console.WriteLine( $"Setting {args.LocationName} to {args.Value}." );
args.ProceedSetValue();
}
}
public struct Point
{
public Point( int x, int y )
{
this.X = x;
this.Y = y;
}
public int X;
public int Y;
public override string ToString() => $"({this.X}, {this.Y})";
}
public class Shape
{
[Trace]
private Point location;
public void MoveRight( int distance )
{
// Copies the structure to a local variable, then assigns it as a whole, which the aspect intercepts.
Point location = this.location;
location.X += distance;
this.location = location;
}
public string Describe() => $"Shape at {this.location}";
}
public static class Program
{
public static void Main()
{
Shape shape = new();
shape.MoveRight( 5 );
Console.WriteLine( shape.Describe() );
}
}
Setting location to (5, 0).
Shape at (5, 0)
See Also
Reference
LocationInterceptionAspect
GetValue<T>(T)
Other Resources