restricciones de tipo de parámetro con nombre

Estoy diseñando una clase de atributo personalizado.

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

Entonces estoy intentando asignar este atributo a una propiedad en una clase contigua:

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

Sin embargo, el comstackdor se queja de lo siguiente:

‘ValRange’ no es un argumento de atributo con nombre válido porque no es un tipo de parámetro de atributo válido

También intenté convertir la clase ValueRange en una struct con la esperanza de que se convierta en un tipo de valor que pueda resolver el problema. ¿Hay alguna manera de evitar esto?

¿Hay alguna manera de evitar esto?

No.

Para más detalles, le remito a la sección 17.1.3 de la especificación C # 4, que reproduzco aquí para su conveniencia:


Los tipos de parámetros posicionales y con nombre para una clase de atributo están limitados a los tipos de parámetros de atributo, que son:

  • Uno de los siguientes tipos: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  • El tipo de objeto.
  • El tipo System.Type.
  • Un tipo de enumeración, siempre que tenga accesibilidad pública y los tipos en los que está nested (si corresponde) también tienen accesibilidad pública.
  • Matrices unidimensionales de los tipos anteriores.

Un argumento de constructor o un campo público que no tiene uno de estos tipos, no se puede usar como un parámetro posicional o con nombre en una especificación de atributo.


Recuerde, el punto de un atributo es, en el momento de la comstackción, agregar información a los metadatos asociados con la entidad en la que ha colocado el atributo. Eso significa que toda la información asociada con ese atributo debe tener una forma clara y clara de serializarla dentro y fuera de los metadatos. Al restringir el conjunto de tipos legales a un pequeño subconjunto de todos los tipos posibles, nos aseguramos de que el comstackdor siempre pueda emitir metadatos legales que el consumidor pueda entender.

Los valores de los parámetros de atributo deben poder resolverse en tiempo de comstackción (es decir, constantes).

Ver Attribute Parameter Types en MSDN:

Los valores pasados ​​a los atributos deben ser conocidos por el comstackdor en el momento de la comstackción.

Si puede crear un ValueRange que sea una constante, puede usarlo.

Los parámetros de atributo deben ser valores de los siguientes tipos (citando el artículo):

  • Tipos simples (bool, byte, char, short, int, long, float y double)
  • cuerda
  • Tipo de sistema
  • enums
  • objeto (el argumento de un parámetro de atributo de tipo objeto debe ser un valor constante de uno de los tipos anteriores).
  • Arreglos unidimensionales de cualquiera de los tipos anteriores

Edición: Se cambió la “constante de tiempo de comstackción” por “valor”, ya que los tipos y las matrices no son constantes (gracias al comentarista que señaló esto (y posteriormente eliminó su comentario por alguna razón …))

Los atributos solo pueden recibir constantes de tiempo de comstackción como parámetros (por ejemplo, 3, “hello”, typeof (MyClass), “ruta a un recurso que define los datos no constantes que necesita”).

El último ejemplo (que pasa un tipo) que proporcioné puede ayudarlo a diseñar una solución alternativa (pasar un tipo implementando una interfaz con el método que necesita).

¿Hay alguna manera de evitar esto?

Sí.

Puede hacer que su atributo use una propiedad Type y luego usar tipos que implementen una interfaz definida, para lo cual el código que procesa ese atributo debería asumir , y como tal también crea un requisito implícito , pero con suerte documentado , para sus clientes:

 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; } } 

Esto no es diferente a muchas clases en el espacio de nombres System.ComponentModel , como DesignerAttribute .