Open sandboxFocusImprove this doc

PS0212: Intercepted Field Passed by Reference from Another Type

Severity Error
Reported by PostSharp
Message The field "<Field>" cannot be loaded by reference in method "<Method>" because an aspect is applied to it. To pass the field by reference or call a method of the field, copy it to a local variable, use the local variable, and assign the local variable back to the field.

Cause

When an aspect such as a LocationInterceptionAspect is applied to a field, PostSharp turns the field into a property of the same name, renames the field and makes it private, and replaces every read and write of the field in the project with a call to the property. See Intercepting Properties and Fields.

Code that loads the address of the field cannot be redirected to a property: passing the field as a ref or out argument, or calling a method of a field of a structure type. When this code is in the type that declares the field, PostSharp reports the warning PS0089 and the access bypasses the aspect. When it is in another type, the private field is no longer accessible, so PostSharp reports PS0212 at the location of the access.

In the following example, Program.Main passes the Port field, which the [TrackChanges] aspect intercepts, as an out argument.

using System;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.PS0212_Error;

[PSerializable]
public class TrackChangesAttribute : LocationInterceptionAspect
{
    public override void OnSetValue( LocationInterceptionArgs args )
    {
        Console.WriteLine( $"Setting {args.LocationName} to {args.Value}." );
        args.ProceedSetValue();
    }
}

public class ServerSettings
{
    [TrackChanges]
    public int Port;
}

public static class SettingsReader
{
    public static bool TryReadPort( string text, out int port ) => int.TryParse( text, out port );
}

public static class Program
{
    public static void Main()
    {
        var settings = new ServerSettings();

        // The field is passed by reference, which bypasses the aspect.
        SettingsReader.TryReadPort( "8080", out settings.Port );

        Console.WriteLine( $"Port: {settings.Port}" );
    }
}
error PS0212: PS0212_Error.cs(35,9): The field "Errors.PS0212_Error.ServerSettings.Port" cannot be loaded by reference in method "Errors.PS0212_Error.Program.Main" because an aspect is applied to it. To pass the field by reference or call a method of the field, copy it to a local variable, use the local variable, and assign the local variable back to the field.

Resolution

Do not load the address of the field. Pass a local variable by reference instead, and assign it to the field afterwards: the assignment goes through the aspect. To call a method of a field of a structure type, copy the field to a local variable, call the method on the local variable, and assign it back to the field.

Making the field private does not help, since the other type then cannot access it at all, and a property cannot be passed by reference in C#.

In the following example, a local variable receives the value, and the aspect intercepts its assignment to the field.

using System;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.PS0212_Fixed;

[PSerializable]
public class TrackChangesAttribute : LocationInterceptionAspect
{
    public override void OnSetValue( LocationInterceptionArgs args )
    {
        Console.WriteLine( $"Setting {args.LocationName} to {args.Value}." );
        args.ProceedSetValue();
    }
}

public class ServerSettings
{
    [TrackChanges]
    public int Port;
}

public static class SettingsReader
{
    public static bool TryReadPort( string text, out int port ) => int.TryParse( text, out port );
}

public static class Program
{
    public static void Main()
    {
        var settings = new ServerSettings();

        // A local variable is passed by reference, then assigned to the field, which runs the aspect.
        if ( SettingsReader.TryReadPort( "8080", out var port ) )
        {
            settings.Port = port;
        }

        Console.WriteLine( $"Port: {settings.Port}" );
    }
}
Setting Port to 8080.
Port: 8080

See Also

Reference

LocationInterceptionAspect

Other Resources

Intercepting Properties and Fields