-->

Passing static array in attribute

2020-08-23 01:04发布

问题:

Is it possible to circumvent the following restriction:

Create a static readonly array in a class:

public class A
{
    public static readonly int[] Months = new int[] { 1, 2, 3};
}

Then pass it as a parameter to an attribute:

public class FooAttribute : Attribute
{
    public int[] Nums { get; set; }

    FooAttribute()
    {
    }
}

--- Let's say Box is a property of class A ---

[Foo(Nums = A.Months)]
public string Box { get; set; }

I know this won't compile and will result in this error:

"An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type".

Is it possible to go around this somehow to be able to use the static array ? I'm asking since this will be much more convenient maintenance-wise, since I have a lot of properties.

Thanks in advance.

回答1:

Unfortunately this is not possible. The attributes (including the values of their arguments) are placed into the assembly metadata by the compiler so it has to be able to evaluate them at compile time (hence the restriction to constant expressions; the exception for array creation expressions was obviously made because otherwise you could not have array arguments at all).

In contrast, the code that actually initializes A.Months is only executed at runtime.



回答2:

No, basically.

You could, however, subclass the attribute and use that, i.e.

class AwesomeFooAttribute : FooAttribute {
    public AwesomeFooAttribute() : FooAttribute(A.Months) {}
}

or:

class AwesomeFooAttribute : FooAttribute {
    public AwesomeFooAttribute() {
        Nums = A.Months;
    }
}

and decorate with [AwesomeFoo] instead. If you use reflection to look for FooAttribute, it will work as expected:

[AwesomeFoo]
static class Program
{
    static void Main()
    {
        var foo = (FooAttribute)Attribute.GetCustomAttribute(
            typeof(Program), typeof(FooAttribute));
        if (foo != null)
        {
            int[] nums = foo.Nums; // 1,2,3
        }
    }
}

You could perhaps nest this inside A, so you are decorating with:

[A.FooMonths]

or similar



回答3:

Short answer: No.

But you can refer to the int array by key:

public class A
{
    public static readonly Dictionary<int, int[]> NumsArrays 
              = new[]{{1, new[]{1,1,1}}, {2, new[]{2,2,2}}, {3, new[]{3,3,3}}};
    public const int Num1 = 1;
    public const int Num2 = 2;
    public const int Num3 = 3;
}

public class FooAttribute : Attribute
{
    public int NumsId { get; set; }

    FooAttribute()
    {
    }
}

[Foo(NumsID = A.Num3)]
public string Box { get; set; }

//Evaluation:
int id = (FooAttribute) Attribute.GetCustomAttribute(type, typeof (FooAttribute));
int[] result = A.NumsArrays[id];//result is {3,3,3}