Named Parameter Type Constraints

I create my own attribute class.

public class MyAttr: Attribute { public ValueRange ValRange { get; set; } } 

Then I try to assign this attribute to a property in a neighboring class:

 public class Foo { [MyAttr(ValRange= new ValueRange())] public string Prop { get; set; } } 

However, the compiler complains about the following:

"ValRange" is not a valid attribute name argument because it is not a valid attribute parameter type

I also tried converting the ValueRange class to struct in the hope that becoming a value type could solve the problem. Is there any way around this?

+7
source share
5 answers

Is there any way around this?

Not.

See section 17.1.3 of the C # 4 specification, which I reproduce here for your convenience, for more details:


The types of positional and named parameters for an attribute class are limited to types of attribute parameters, which are:

  • One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  • Type object.
  • Type System.Type.
  • The type of enumeration, if it has public accessibility, and the types in which it is nested (if any) also have public accessibility.
  • One-dimensional arrays of the above types.

A constructor argument or a public field that does not have one of these types cannot be used as a positional or named parameter in an attribute specification.


Remember that an attribute point is at compile time to add information to the metadata associated with the object on which you placed the attribute. This means that all information associated with this attribute must have a clearly defined, unambiguous way to serialize it into and out of metadata. By limiting the set of legal types to a small subset of all possible types, we guarantee that the compiler can always fix legal metadata that the consumer can understand.

+21
source

Attribute parameter values ​​must be resolvable at compile time (for example, constants).

See Attribute Parameter Types on MSDN:

The values ​​passed to the attributes must be known to the compiler at compile time.

If you can create a ValueRange , which is a constant, you can use it.

+2
source

Attribute parameters must be of the following types (citing the article):

  • Simple types (bool, byte, char, short, int, long, float and double)
  • line
  • System.Type
  • transfers
  • object (The argument argument of the attribute of the type object must be a constant value of one of the above types.)
  • One-dimensional arrays of any of these types

Edit: changed the “compile-time constant” to “value”, because types and arrays are not constants (thanks to the commentator who pointed this out (and subsequently deleted his comment for some reason ...))

+1
source

Attributes can only receive compile-time parameters as parameters (for example, 3, "hi", typeof (MyClass), "path to the resource that defines any non-persistent data that you need").

The last example (type passing) that I gave can help you develop a workaround (passing a type that implements an interface with the method you need).

+1
source

Is there any way around this?

Yes.

You can use your attribute with the Type attribute, and then use types that implement a specific interface, for which the code processing this attribute should be used, and, as such, also creates an implicit, but, hopefully, documentary requirement for your 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 like many classes in the System.ComponentModel namespace, such as DesignerAttribute .

+1
source

All Articles