So I have a custom attribute, let’s call it MyCustomAttribute, which has a constructor like so:
public MyCustomAttribute(int? i)
{
// Code goes here
}
and declares a property:
public int? DefaultProperty { get; set; }
Now if I wanted to use the property, I’d need to pass in an int or null, well that’s what I’d expect.
But this gives a compiler error:
[MyCustomAttribute(1, DefaultProperty = 1)]
public int? MyProperty { get; set; }
and so does this:
[MyCustomAttribute(null,DefaultProperty = null)]
public int? MyProperty { get; set; }
The error is: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type for both the constructor and the property.
Why is this? If I change the constructor to take an int, I can pass in 0, but not null, which sort of defeats the purpose of the value (which can sometimes be null)
The reason why is that while both
0andnullare constant the actual expression needed to initialize the constructor parameter is not. Both require a conversion to be a validint?expression. Under the hood it essentially generates the followingThis expression not being constant is not legal as an attribute argument
EDIT
dtb asked if this is illegal in attribute values why is it legal to have a default parameter for a nullable parameter?
What you need to consider is who / what is interpreting the values.
For attributes arguments the value is interpreted by the CLR. The rules concerning legal attribute values haven’t really changed since 1.0. Nullable didn’t exist then hence the CLR doesn’t understand nullable initialization patterns.
For default arguments the value is interpreted by the compiler at the call site of the method. The compilers understands nullable values and has a bit more room to work with as it can create non-constant expressions at the call site based on the values in IL.
How does it actually work though? First the compiler will actually encode constants and
nulldifferently in ILAt the call site the compiler examines these values and creates the appropriate (non-constant) expression for the parameter value.