Open sandboxFocusImprove this doc

LA0279: Instance Field Requested in a Type with Explicit Layout

Severity Error
Reported by PostSharp
Message The aspect "<Aspect>" applied to "<Target>" attempts to introduce an instance field <Name>of type "<FieldType>" in "<TargetType>", which has an explicit layout: an instance field introduced into it would have no offset. Introduce a static field, or declare the field in "<TargetType>" with its offset and set the WhenCompatibleFieldExists property of the IntroduceFieldAdviceInstance to Ignore.

Cause

In a class or a struct with an explicit layout, [StructLayout( LayoutKind.Explicit )], every instance field must have its offset, given by FieldOffsetAttribute. When an aspect attempts to introduce an instance field with an IntroduceFieldAdviceInstance, PostSharp cannot choose this offset for a new field. It reports LA0279 when such a request must introduce a new field: no existing field matches the request, or its WhenCompatibleFieldExists property is IntroduceAnother. A request for a static field, or a request that uses an existing field, is not affected. LA0279 is always an error.

<Name> is the requested name in quotes followed by a space, or nothing when the request has no name. <Target> is the declaration to which the aspect is applied, where the error is located.

In the following example, the [CountChanges] aspect counts the changes of a Color struct in a Changes field, which the struct does not declare. The fields of Color overlap, so the struct has an explicit layout.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0279_Error;

// Counts the changes of a value in a field named "Changes", which the target type may declare.
[PSerializable]
public sealed class CountChangesAttribute : MethodLevelAspect, IAdviceProvider
{
    public IEnumerable<AdviceInstance> ProvideAdvices( object targetElement )
    {
        yield return new IntroduceFieldAdviceInstance( typeof(int) )
        {
            Name = "Changes", WhenCompatibleFieldExists = IntroduceFieldCompatibleAction.Ignore
        };
    }

    [OnMethodSuccessAdvice]
    public static void OnSuccess( [ImportMember( "Changes" )] ref int changes ) => changes++;
}

// The fields of the struct overlap, so each field has an explicit offset.
[StructLayout( LayoutKind.Explicit )]
public struct Color
{
    [FieldOffset( 0 )]
    public uint Argb;

    [FieldOffset( 0 )]
    public byte Blue;

    // The struct declares no "Changes" field, so the aspect requests a new one, which would have no offset.
    [CountChanges]
    public void SetBlue( byte blue ) => this.Blue = blue;
}

public static class Program
{
    public static void Main()
    {
        Color color = new Color { Argb = 0xFF000000 };
        color.SetBlue( 0x80 );
        Console.WriteLine( $"ARGB: {color.Argb:X8}." );
    }
}
error LA0279: LA0279_Error.cs(38,17): The aspect "Errors.LA0279_Error.CountChangesAttribute" applied to "Errors.LA0279_Error.Color.SetBlue(System.Byte)" attempts to introduce an instance field "Changes" of type "int32" in "Errors.LA0279_Error.Color", which has an explicit layout: an instance field introduced into it would have no offset. Introduce a static field, or declare the field in "Errors.LA0279_Error.Color" with its offset and set the WhenCompatibleFieldExists property of the IntroduceFieldAdviceInstance to Ignore.

Resolution

  • Declare the field in the type, with its offset, and make the request use it: the field must have the requested name, or the requested type when the request has no name, and WhenCompatibleFieldExists must be Ignore. With the default value, Fail, PostSharp reports LA0271 instead.
  • Introduce a static field, with IsStatic, when one value for all the objects of the type is enough.

In the following example, the Color struct declares the Changes field at offset 4, and the aspect uses it.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0279_Fixed;

// Counts the changes of a value in a field named "Changes", which the target type may declare.
[PSerializable]
public sealed class CountChangesAttribute : MethodLevelAspect, IAdviceProvider
{
    public IEnumerable<AdviceInstance> ProvideAdvices( object targetElement )
    {
        yield return new IntroduceFieldAdviceInstance( typeof(int) )
        {
            Name = "Changes", WhenCompatibleFieldExists = IntroduceFieldCompatibleAction.Ignore
        };
    }

    [OnMethodSuccessAdvice]
    public static void OnSuccess( [ImportMember( "Changes" )] ref int changes ) => changes++;
}

// The fields of the struct overlap, so each field has an explicit offset.
[StructLayout( LayoutKind.Explicit )]
public struct Color
{
    [FieldOffset( 0 )]
    public uint Argb;

    [FieldOffset( 0 )]
    public byte Blue;

    // The struct declares the "Changes" field with its offset, and the aspect uses it.
    [FieldOffset( 4 )]
    public int Changes;

    [CountChanges]
    public void SetBlue( byte blue ) => this.Blue = blue;
}

public static class Program
{
    public static void Main()
    {
        Color color = new Color { Argb = 0xFF000000 };
        color.SetBlue( 0x80 );
        Console.WriteLine( $"ARGB: {color.Argb:X8}. Changes: {color.Changes}." );
    }
}
ARGB: FF000080. Changes: 1.

See Also

Reference

IntroduceFieldAdviceInstance
IntroduceFieldCompatibleAction
StructLayoutAttribute

Other Resources

Providing Advices Dynamically