2012-03-22 55 views
6

使用anonymous methods您可以自C#2.0以来创建空委托。如何使用表达式树创建空的委托?

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

这是例如useful to prevent having to do the null check when invoking events

如何使用Expression Trees创建相同的行为?

我现在看到的唯一可能的选择是使用Expression.Lambda(),但据我所知,这将需要大量额外的工作。

回答

2

事实证明,它不是使用Expression.Lambda()很多工作。不过,我仍然对其他可能的答案感兴趣。

我确实需要一个辅助方法,这是我以前写的:

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

当你有EventInfo你可以为它,如下所示创建一个空的λ:

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

表达式树本身的目的通常具有表达式,而不是原始设计中的语句。

在C#3中根本没有办法表达一个表达式树,它的主体是一个空的语句块。最近,表达式树库已扩展为允许使用语句,但C#语义分析规则未更新以利用它;您仍然无法将语句lambda转换为表达式树。

+0

我想这就是为什么['Expression.Empty'](http://msdn.microsoft.com/en-us/library/dd294122.aspx)只能从C#4.0开始使用?我并不是真的想把一个陈述变成一个表达树,而是反过来。表达式树语句导致“空”的委托。我想我现在发现了[_a_解决方案](http://stackoverflow.com/a/9823691/590790),但我可能会感到困惑。 :) – 2012-03-22 14:03:49

+2

@StevenJeuris:啊,我误解了你的问题的主旨。是的,您可以构建一个逻辑上为空语句块的“手动”表达式树。没有办法让* C#编译器*通过lambda转换为您做到这一点,这是我的观点。 – 2012-03-22 14:49:44

1

扩大史蒂芬回答有点位 - 我需要类似的功能为两者创建一个空的代理 - Action和Func类型 - 以下是我为该任务创建的帮助程序:

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