【问题标题】:How to create an empty delegate using Expression Trees?如何使用表达式树创建一个空委托?
【发布时间】:2023-03-05 06:35:01
【问题描述】:

使用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(),但据我所知,这需要很多额外的工作。

【问题讨论】:

    标签: c# events delegates expression-trees


    【解决方案1】:

    就其目的而言,表达式树始终具有用于主体的表达式,而不是原始设计中的语句。

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

    【讨论】:

    • 我猜这就是为什么Expression.Empty 只能从 C# 4.0 开始使用?我并不是真的想把一个语句变成一个表达式树,而是反过来。导致“空”委托的表达式树语句。我想我找到了a solution now,但我可能只是感到困惑。 :)
    • @StevenJeuris:啊,我误解了你问题的主旨。是的,您可以“手动”构造一个逻辑上是空语句块的表达式树。没有办法让 C# 编译器 通过 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 时,您可以为它创建一个空的 lambda,如下所示:

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

    【讨论】:

      【解决方案3】:

      稍微扩展 Steven 的答案 - 我需要类似的功能来为 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();
              }
          }
      

      【讨论】:

        猜你喜欢
        • 2011-03-07
        • 2011-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多