2011-10-09 22 views
35

¿Es posible tener un delegado como parámetro de un atributo?¿Es posible tener un delegado como parámetro de atributo?

De esta manera:

public delegate IPropertySet ConnectionPropertiesDelegate(); 

public static class TestDelegate 
{ 
    public static IPropertySet GetConnection() 
    { 
     return new PropertySetClass(); 
    } 
} 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface,AllowMultiple=false,Inherited=true)] 
public class WorkspaceAttribute : Attribute 
{ 
    public ConnectionPropertiesDelegate ConnectionDelegate { get; set; } 

    public WorkspaceAttribute(ConnectionPropertiesDelegate connectionDelegate) 
    { 
     ConnectionDelegate = connectionDelegate; 
    } 
} 

[Workspace(TestDelegate.GetConnection)] 
public class Test 
{ 
} 

Y si no es posible, ¿cuáles son las alternativas razonables?

Respuesta

29

No, no puede tener un delegado como parámetro de constructor de atributo. Ver tipos disponibles: Attribute parameter types
Como solución (aunque es hacky y propenso a errores) se puede crear un delegate with reflection: tipo base Atributo abstracta

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)] 
public class WorkspaceAttribute : Attribute 
{ 
    public ConnectionPropertiesDelegate ConnectionDelegate { get; set; } 

    public WorkspaceAttribute(Type delegateType, string delegateName) 
    { 
     ConnectionDelegate = (ConnectionPropertiesDelegate)Delegate.CreateDelegate(delegateType, delegateType.GetMethod(delegateName)); 
    } 
} 

[Workspace(typeof(TestDelegate), "GetConnection")] 
public class Test 
{ 
} 
+2

En realidad, es una buena solución. Construí una interfaz específica para hacer esto, pero el delegado es muchísimo más fácil. ¡Gracias! –

+0

Es una solución, pero no es buena. Es exactamente como dijo @nemesv: hacky y propenso a errores, porque si cambias el nombre del método GetConnection a otra cosa usando el menú refactor, la cadena "GetConnection" no cambiará automáticamente. – prostynick

+0

Entonces, ¿existe una posibilidad en C# restringir manualmente los argumentos tipo de constructor? No escuché nada al respecto, pero como vemos en el tipo 'Attribute' es posible. ¿Cómo? – monstr

9

Otra solución posible es la creación de método abstracto que coinciden con la definición de delegado, y luego implementando el método en la clase de Atributo concreta.

Tiene las siguientes ventajas:

  • La anotación es más concisa y limpia (DSL similares)
  • Ninguna reflexión
  • fácil de reutilizar

Ejemplo:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple=false, Inherited=true)] 
public abstract class GetConnectionAttribute : Attribute 
{ 
    public abstract IPropertySet GetConnection(); 
} 

public class GetConnectionFromPropertySetAttribute : GetConnectionAttribute 
{ 
    public override IPropertySet GetConnection() 
    { 
     return new PropertySetClass(); 
    } 
} 

[GetConnectionFromPropertySet] 
public class Test 
{ 
} 
1

Lo resolví usando una enumeración y una matriz de asignación de delegados. Aunque realmente me gusta la idea de usar la herencia, en mi caso, necesitaría escribir varias clases secundarias para hacer cosas relativamente simples. Esto también debería ser refactorizable. El único inconveniente es que debe asegurarse de hacer que el índice del delegado en la matriz corresponda al valor enum.

public delegate string FormatterFunc(string val); 

public enum Formatter 
{ 
    None, 
    PhoneNumberFormatter 
} 

public static readonly FormatterFunc[] FormatterMappings = { null, PhoneNumberFormatter }; 

public string SomeFunction(string zzz) 
{ 
    //The property in the attribute is named CustomFormatter 
    return FormatterMappings[(int)YourAttributeHere.CustomFormatter](zzz); 
} 
Cuestiones relacionadas