2011-10-09 14 views
35

È possibile avere un delegato come parametro di un attributo?È possibile avere un delegato come parametro di attributo?

Ti piace questa:

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

E se non è possibile, quali sono le alternative sensate?

risposta

29

No, non è possibile avere un delegato come parametro del costruttore di attributi. Vedere tipi disponibili: Attribute parameter types
Per aggirare il problema (anche se è hacky e soggetto a errori) è possibile creare una delegate with reflection: astratto tipo di base Attribute

[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

In realtà è una buona soluzione. Ho creato un'interfaccia specifica per farlo, ma il delegato è molto più semplice. Grazie! –

+0

È una soluzione, ma non una buona soluzione. È esattamente come @nemesv ha detto: hacky e soggetto a errori, perché se si modifica il nome del metodo GetConnection in qualcos'altro utilizzando il menu Refactor la stringa "GetConnection" non verrà modificata automaticamente. – prostynick

+0

Quindi c'è una possibilità in C# limitare manualmente il tipo di argomento del costruttore? Non ho sentito nulla al riguardo, ma come vediamo nel tipo "Attributo" è possibile. Come? – monstr

9

Altra possibile soluzione è la creazione di metodo astratto che corrispondono alla definizione di delegato e quindi implementare il metodo in concreto Attribute class.

Ha seguenti vantaggi:

  • annotazione è più conciso e pulito (DSL simili)
  • No riflessione
  • facile da riutilizzare

Esempio:

[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

Ho risolto questo utilizzando un enum e una matrice di mappatura dei delegati. Anche se mi piace molto l'idea di usare l'ereditarietà, nel mio scenario che mi richiederebbe di scrivere diverse classi figlie per fare cose relativamente semplici. Anche questo dovrebbe essere rifattorico. L'unico inconveniente è che devi assicurarti che l'indice del delegato nella matrice corrisponda al valore 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); 
} 
Problemi correlati