问题
I am designing a custom attribute class.
public class MyAttr: Attribute
{
public ValueRange ValRange { get; set; }
}
Then I am attempting to assign this attribute to a property in an adjoining class:
public class Foo
{
[MyAttr(ValRange= new ValueRange())]
public string Prop { get; set; }
}
However, the compiler is complaining the following:
'ValRange' is not a valid named attribute argument because it is not a valid attribute parameter type
I also tried converting the ValueRange class to a struct
in hopes that become a value type might solve the problem. Is there any way around this?
回答1:
Is there any way around this?
No.
For more details I refer you to section 17.1.3 of the C# 4 specification, which I reproduce here for your convenience:
The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are:
- One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
- The type object.
- The type System.Type.
- An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility.
- Single-dimensional arrays of the above types.
A constructor argument or public field which does not have one of these types, cannot be used as a positional or named parameter in an attribute specification.
Remember, the point of an attribute is to at compile time add information to the metadata associated with the entity upon which you've placed the attribute. That means that all the information associated with that attribute must have a well-defined, unambiguous way to serialize it into and out of metadata. By restricting the set of legal types to a small subset of all possible types we ensure that the compiler can always emit legal metadata that the consumer can understand.
回答2:
Attribute parameter values need to be resolvable at compile time (i.e constants).
See Attribute Parameter Types on MSDN:
Values passed to attributes must be known to the compiler at compile time.
If you can create a ValueRange
that is a constant, you can use it.
回答3:
Attribute parameters must be values of the following types (quoting the article):
- Simple types (bool, byte, char, short, int, long, float, and double)
- string
- System.Type
- enums
- object (The argument to an attribute parameter of type object must be a constant value of one of the above types.)
- One-dimensional arrays of any of the above types
Edit: Changed "compile-time constant" to "value", since types and arrays are not constants (thanks to the commenter who pointed this out (and subsequently deleted his comment for some reason...))
回答4:
Attributes can only receive compile-time-constants as parameters (e.g. 3, "hello", typeof(MyClass), "path to a resource defining whatever non constant data you need").
The last example (passing a type) I gave may help you design a workaround (pass a type implementing an interface with the method you need).
回答5:
Is there any way around this?
Yes.
You can have your attribute use a Type
property and then use types that implement a defined interface, for which the code that processes that attribute would have to assume, and as such also create an implicit, but hopefully documented, requirement to its clients:
public interface IValueRange {
int Start { get; }
int End { get; }
}
public class MyAttr : Attribute {
// The used type must implement IValueRange
public Type ValueRangeType { get; set; }
}
// ....
public class Foo {
class FooValueRange : IValueRange {
public int Start { get { return 10; } }
public int End { get { return 20; } }
}
[MyAttr(ValueRangeType = typeof(FooValueRange))]
public string Prop { get; set; }
}
This is not unlike many classes in the System.ComponentModel
namespace, like DesignerAttribute.
来源:https://stackoverflow.com/questions/5809755/named-parameter-type-constraints