2012-03-22 8 views
6

Usando anonymous methods puede crear delegados vacíos desde C# 2.0.¿Cómo crear un delegado vacío usando Expression Trees?

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

Esto es, por ejemplo, useful to prevent having to do the null check when invoking events.

¿Cómo puedo crear el mismo comportamiento usando Expression Trees?

La única opción posible que veo ahora es usar Expression.Lambda(), pero hasta donde puedo decirlo, esto requeriría mucho trabajo extra.

Respuesta

2

Como resulta que no es que mucho trabajo usando Expression.Lambda(). Sin embargo, todavía estoy interesado en posibles otras respuestas.

que tenía necesidad de un método de ayuda que escribí anteriormente:

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

Cuando se tiene EventInfo puede crear una lambda vacío para ello de la siguiente manera:

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 árbol de expresiones, por naturaleza de su propósito, siempre tiene una expresión para un cuerpo en lugar de una declaración en el diseño original.

En C# 3 no había manera de expresar un árbol de expresiones cuyo cuerpo es un bloque de instrucciones vacío. Más recientemente, la biblioteca del árbol de expresiones se ha ampliado para permitir declaraciones, pero las reglas de análisis semántico de C# no se actualizaron para aprovechar eso; todavía no puedes convertir una declaración lambda en un árbol de expresiones.

+0

Supongo que es por eso que ['Expression.Empty'] (http://msdn.microsoft.com/en-us/library/dd294122.aspx) solo está disponible a partir de C# 4.0? Realmente no estaba buscando convertir una declaración en un árbol de expresiones, sino al revés. Una declaración del árbol de expresiones que da como resultado un delegado 'vacío'. Creo que encontré [_a_ solution now] (http://stackoverflow.com/a/9823691/590790), pero podría estar confundido. :) –

+2

@StevenJeuris: Ah, he entendido mal el impulso de su pregunta. Sí, puede construir un árbol de expresiones "manualmente" que lógicamente es un bloque de instrucciones vacío. No hay forma de que * el compilador de C# * lo haga por usted a través de una conversión lambda, fue mi punto. –

1

La ampliación de la Steven responder a una pequeña bit - Necesitaba una funcionalidad similar para crear un delegado vacío para ambos - Tipos de acción y de Func - El siguiente es el helper que creé para esa tarea:

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(); 
     } 
    } 
Cuestiones relacionadas