2012-03-22 7 views
6

Utilizzando anonymous methods è possibile creare delegati vuoti da C# 2.0.Come creare un delegato vuoto usando le Expression Trees?

public event EventHandler SomeEvent = delegate {}; 
public event Action OtherEvent = delegate {}; 

Questo è per es. useful to prevent having to do the null check when invoking events.

Come posso creare lo stesso comportamento utilizzando Expression Trees?

L'unica opzione possibile che vedo ora è utilizzare Expression.Lambda(), ma per quanto posso dire questo richiederebbe molto lavoro extra.

risposta

2

Come risulta non è che molto lavoro utilizzando Expression.Lambda(). Tuttavia, sono ancora interessato a possibili altre risposte.

ho bisogno di un metodo di supporto che ho scritto in precedenza:

/// <summary> 
/// The name of the Invoke method of a Delegate. 
/// </summary> 
const string InvokeMethod = "Invoke"; 

/// <summary> 
/// Get method info for a specified delegate type. 
/// </summary> 
/// <param name = "delegateType">The delegate type to get info for.</param> 
/// <returns>The method info for the given delegate type.</returns> 
public static MethodInfo MethodInfoFromDelegateType(Type delegateType) 
{ 
    Contract.Requires(
     delegateType.IsSubclassOf(typeof(MulticastDelegate)), 
     "Given type should be a delegate."); 

    return delegateType.GetMethod(InvokeMethod); 
} 

Quando hai EventInfo è possibile creare un lambda vuoto per come segue:

EventInfo _event; 

... 

MethodInfo delegateInfo 
    = DelegateHelper.MethodInfoFromDelegateType(_event.EventHandlerType); 
ParameterExpression[] parameters = delegateInfo 
    .GetParameters() 
    .Select(p => Expression.Parameter(p.ParameterType)) 
    .ToArray(); 
Delegate emptyDelegate = Expression.Lambda(
    _event.EventHandlerType, 
    Expression.Empty(), "EmptyDelegate", true, parameters).Compile(); 
5

Un albero di espressioni, per natura del suo scopo, ha sempre un'espressione per un corpo piuttosto che un'affermazione nel progetto originale.

In C# 3 non c'era assolutamente modo di esprimere un albero di espressioni il cui corpo è un blocco di istruzioni vuoto. Più recentemente, la libreria dell'albero delle espressioni è stata estesa per consentire le istruzioni, ma le regole di analisi semantica del C# non sono state aggiornate per trarne vantaggio; non riesci ancora a trasformare una frase lambda in un albero delle espressioni.

+0

Immagino che sia il motivo per cui ['Expression.Empty'] (http://msdn.microsoft.com/en-us/library/dd294122.aspx) è disponibile solo a partire da C# 4.0? Non volevo davvero trasformare una frase in un albero di espressione, piuttosto il contrario. Una dichiarazione ad albero delle espressioni che risulta in un delegato 'vuoto'. Penso di aver trovato [_a_ solution now] (http://stackoverflow.com/a/9823691/590790), ma potrei semplicemente essere confuso. :) –

+2

@StevenJeuris: Ah, ho frainteso la spinta della tua domanda. Sì, puoi costruire un albero di espressioni "manualmente" che è logicamente un blocco di istruzioni vuoto. Non c'è modo di fare * il compilatore C# * farlo per te tramite una conversione lambda, era il mio punto. –

1

Espansione del Steven rispondere un po ' bit - Avevo bisogno di funzionalità simili per creare un delegato vuoto per entrambi i tipi: Action e Func: follow è l'helper che ho creato per quell'attività:

static class MethodInfoHelper<T> 
    { 
     static MethodInfoHelper() 
     { 
      VerifyTypeIsDelegate(); 
     } 

     public static void VerifyTypeIsDelegate() 
     { 
      //Lets make sure this is only ever used in code for Func<> types 
      if (!typeof(T).IsSubclassOf(typeof(Delegate))) 
      { 
       throw new InvalidOperationException(typeof(T).Name + " is not a delegate type"); 
      } 

      if (!typeof(T).Name.StartsWith("Func") && !typeof(T).Name.StartsWith("Action")) 
      { 
       throw new InvalidOperationException(typeof(T).Name + " is not a Func nor an Action"); 
      } 
     } 

     private static bool HasReturnType 
     { 
      get { return typeof(T).Name.StartsWith("Func"); } 
     } 

     /// <summary> 
     /// Creates an empty delegate of given type 
     /// </summary> 
     /// <typeparam name="T">Func or Action type to be created</typeparam> 
     /// <returns>A delegate to expression doing nothing</returns> 
     public static T CreateEmptyDelegate() 
     { 
      Type funcType = typeof(T); 
      Type[] genericArgs = funcType.GenericTypeArguments; 

      List<ParameterExpression> paramsExpressions = new List<ParameterExpression>(); 
      for (int paramIdx = 0; paramIdx < (HasReturnType ? genericArgs.Length - 1 : genericArgs.Length); paramIdx++) 
      { 
       Type argType = genericArgs[paramIdx]; 

       ParameterExpression argExpression = Expression.Parameter(argType, "arg" + paramIdx); 
       paramsExpressions.Add(argExpression); 
      } 

      Type returnType = HasReturnType ? genericArgs.Last() : typeof(void); 

      DefaultExpression emptyExpression = (DefaultExpression)typeof(DefaultExpression).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, 
       new Type[] { typeof(Type) }, null).Invoke(new[] { returnType }); 

      Expression<T> resultingExpression = Expression.Lambda<T>(
       emptyExpression, "EmptyDelegate", true, paramsExpressions); 

      return resultingExpression.Compile(); 
     } 
    }