12

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.

2
  • 1
    should "readonly" be "const" conceptually? Commented May 23, 2013 at 9:43
  • 1
    Maybe, but they are different: The readonly keyword is different from the const keyword. A const field can only be initialized at the declaration of the field. A readonly field can be initialized either at the declaration or in a constructor. Therefore, readonly fields can have different values depending on the constructor used. Commented May 23, 2013 at 9:45

3 Answers 3

12

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.

Sign up to request clarification or add additional context in comments.

1 Comment

or in other words, attribute needs to be determined at compile time, and readonly vars are assigned at runtime? Is this understanding correct?
12

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

6 Comments

Hmmm... and I wonder: what happens if you reflect in a reflection-only context?
@Jon yeah, then you're scuppered. The metadata doesn't know anything at all about what happens inside. If you only have access to metadata, all you will be able to tell is "there is an AwesomeFooAttribute, which I can tell is itself a FooAttribute". Most people, however, aren't working in reflection-only contexts. As it happens, I do do a lot of that (via IKVM.Reflection, usually) - and I feel your pain :)
For a minute there I thought that somehow the code was equivalent and the compiler went out of its way to see that the field is initialized with a new array... which would theoretically be possible in this specific instance but is of course ridiculous as a feature because it wouldn't work in many cases. Thanks for clearing that up.
@Jon indeed, this is 99% runtime; but a useful trick since the majority of cases are going to be using runtime, not reflection-only
@MarcGravell: Thanks Marc, interesting solution, though I can't afford to have a new attribute and do that in the constructor :-( Will keep that in mind when the opportunity arises though!
|
5

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}

1 Comment

Thx, const helps

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.