How to create an empty delegate using Expression Trees?
Asked Answered
A

3

6

Using anonymous methods you can create empty delegates since C# 2.0.

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

This is e.g. useful to prevent having to do the null check when invoking events.

How can I create the same behavior using Expression Trees?

The only possible option I see now is to use Expression.Lambda(), but as far as I can tell this would require a lot of extra work.

Atalante answered 22/3, 2012 at 13:40 Comment(0)
A
2

As it turns out it isn't that much work using Expression.Lambda(). However, I'm still interested in possible other answers.

I did need a helper method which I wrote previously:

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

When you have EventInfo you can create an empty lambda for it as follows:

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();
Atalante answered 22/3, 2012 at 13:54 Comment(0)
P
5

An expression tree, by nature of its purpose, always has an expression for a body rather than a statement in the original design.

In C# 3 there was no way at all to express an expression tree whose body is an empty statement block. More recently, the expression tree library has been extended to allow for statements, but the C# semantic analysis rules were not updated to take advantage of that; you still cannot turn a statement lambda into an expression tree.

Pieper answered 22/3, 2012 at 13:51 Comment(2)
I guess that is why Expression.Empty is only available starting from C# 4.0? I wasn't really looking to turn a statement into an expression tree, rather the other way around. An expression tree statement which results in an 'empty' delegate. I think I found a solution now, but I might just be confused. :)Atalante
@StevenJeuris: Ah, I misunderstood the thrust of your question. Yes, you can construct an expression tree "manually" that is logically an empty statement block. There is no way to make the C# compiler do that for you via a lambda conversion, was my point.Pieper
A
2

As it turns out it isn't that much work using Expression.Lambda(). However, I'm still interested in possible other answers.

I did need a helper method which I wrote previously:

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

When you have EventInfo you can create an empty lambda for it as follows:

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();
Atalante answered 22/3, 2012 at 13:54 Comment(0)
I
1

Expanding the Steven answer a little bit - I needed similar functionality to create an empty delegate for both - Action and Func types - following is the helper that I created for that task:

    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();
        }
    }
Influence answered 13/3, 2017 at 20:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.