如何使用表达式树创建一个空委托?

时间:2012-03-22 13:40:57

标签: c# events delegates expression-trees

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

3 个答案:

答案 0 :(得分:5)

表达式树,就其目的而言,总是有一个表达式而不是原始设计中的语句。

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

答案 1 :(得分: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();

答案 2 :(得分:1)

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