views:

611

answers:

4

If I have an enum like this

public enum Hungry
{
    Somewhat,
    Very,
    CouldEatMySocks
}

and a custom attribute like this

public class HungerAttribute : Attribute
{
    public Hungry HungerLevel { get; set; }
    public Hungry? NullableHungerLevel { get; set; }
}

I can do this

[Hunger(HungerLevel = Hungry.CouldEatMySocks)]
public class Thing1

but I can't do this

[Hunger(NullableHungerLevel = Hungry.CouldEatMySocks)]
public class Thing2

It generates an error that says "'NullableHungerLevel' is not a valid named attribute argument because it is not a valid attribute parameter type".

Why is that not allowed? I understand that fundamentally it just isn't on the list of accepted types. The valid types seem to be primitives, enums, string, type, and one dimensional arrays of the preceding types.

Is this just an old rule that did not get updated when Nullable came along?

+6  A: 

In your particular case, you have to explicitly declare an initializer:

class Program
{
    [Hunger(null)]
    static void Main(string[] args)
    {
    }

    public class HungerAttribute : Attribute {
        public Hungry HungerLevel { get; set; } 
        public Hungry? NullableHungerLevel { get; set; }

        public HungerAttribute(Hungry? level)
        {
            NullableHungerLevel = level;
        }

        //Or:
        public HungerAttribute( Hungry level)
        {
            HungerLevel = level;
        }
    }

    public enum Hungry { Somewhat, Very, CouldEatMySocks }
}

I understand that you're not going to use both properties.

Shimmy
The question wasn't "how do I do this", the question was "why". This in no way answers the why.
Joe White
Yes. That works. I know how to get around it, but I'm puzzled as to why it does not work as a named parameter. It seems inconsistent to me.
Mike Two
Should I delete this answer? Cuz unfortunetely I ain't got no answers on the why part :(
Shimmy
@Shimmy. Personally I'd leave the answer. It points out the right way to do it in this case. That will still be useful if anyone else ever searches for that error. It still contributes to the conversation. If you do decide to delete it then please move the suggestion on how to fix the issue to a comment.
Mike Two
@Shimmy thank you for leaving this answer up.
Ryan Lanciaux
+10  A: 

Hungry? is equal to Nullable<Hungry>, which in terms mean that

[Hunger(NullableHungerLevel = Hungry.CouldEatMySocks)]

is equal to

[Hunger(NullableHungerLevel = new Nullable<Hungry>(Hungry.CouldEatMySocks))]

Since you can only use constant values in named attribute arguments you will have to resort to Shimmy's solution.

Lars Udengaard
Now that you explain it it is kind of obvious! Thanks
Mike Two
+1  A: 

Attributes may have as only parameters primitives, typeof expressions and array-creation expression.

Nullable is a struct.

Therefore it is not allowed there.

I suspect the assembly file format itself doesn't allow storage of complex types like structs in the place where attribute values are stored.

I don't know of any plans to change that. But I cannot explain why this restriction exist.

codymanix
+1  A: 

I couldn't get @Shimmy solution working. When copy pasted same exact code. I get "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type" error, when I pass null to the attribute.

[Hunger(null)]

static void Main(string[] args) 
{ 
} 
praks