28

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?

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Mike Two
  • 44,935
  • 9
  • 80
  • 96

4 Answers4

31

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.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
Lars Udengaard
  • 1,227
  • 10
  • 21
26

To get around this create another initializer in your Attribute:

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

  public sealed class HungerAttribute : Attribute
  {        
    public Hungry? HungerLevel { get; }
    public bool IsNull => !_HungerLevel.HasValue;

    public HungerAttribute()
    {
    }

    //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 Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
  • The question wasn't "how do I do this", the question was "why". This in no way answers the why. – Joe White Sep 12 '09 at 21:27
  • 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 Sep 12 '09 at 21:29
  • Should I delete this answer? Cuz unfortunetely I ain't got no answers on the why part :( – Shimmy Weitzhandler Sep 12 '09 at 21:32
  • 7
    @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 Sep 12 '09 at 21:37
8

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
  • 28,510
  • 21
  • 92
  • 151
4

Instead of creating nullable enum, you can create default value for that enum. Enum pick default from 1st value, so set your enum like this

public enum Hungry
{
    None,
    Somewhat,
    Very,
    CouldEatMySocks
}

in your code you could do this to check for null

if(default(Hungry) == HungerLevel)//no value has been set
Ariwibawa
  • 627
  • 11
  • 23