C#中的“无操作”委托

时间:2020-07-22 08:27:02

标签: c# delegates

我有一个通用类,例如

public class Foo<T> where T : Delegate {
    private T nop;
    public Foo(T nop) {
        this.nop = nop;
    }
    public T BuildDelegateChain() {
        if(chainAvailable) {
           return Delegate.Combine(...) as T;
        } else {
           return  nop;
        }
    }
    ....
}

因此,我必须为每个实例打电话:

Foo<Action<int>> foo = new Foo<Action<int>>( _ =>{});
Foo<Action<int,int>> foo = new Foo<Action<int,int>>( (_,__) =>{});

是否可以获取与类型T相匹配的默认“无操作”委托? 我正在寻找将构造函数替换为没有任何参数的构造函数的方法。像这样:

...
public Foo() {
    this.nop = Delegate.CreateNop(typeof(T));
}   
...

1 个答案:

答案 0 :(得分:3)

您可以使用Expression类动态创建一个无操作委托。对于具有返回类型的代表,无操作将必须返回default(TReturn)。否则,它只需要是一个空块。然后,我们将缓存委托,以便反射仅发生一次。

using System.Linq;
using System.Linq.Expressions;

public static class DelegateHelper<T> where T : Delegate
{
    public static T NoOp { get; } = BuildNoOpDelegate();

    private static T BuildNoOpDelegate()
    {
        var invoke = typeof(T).GetMethod(nameof(Action.Invoke));

        var paramTypes = invoke.GetParameters().Select(c => c.ParameterType);
        
        // return default(TReturn) or default(Void)
        var body = Expression.Default(invoke.ReturnType);

        var lambda = Expression.Lambda<T>(
            body,
            paramTypes.Select(Expression.Parameter)
        );
        return lambda.Compile();
    }

}

然后使用它:

public class Foo<T> where T : Delegate {
    private T nop;
    public Foo() {
        nop = DelegateHelper<T>.NoOp;
    }
}

有趣的是,这也适用于具有out / ref参数的委托。对于out,我们不需要在方法退出之前做任何特殊的事情来设置值(对于纯正的C#来说是必须的),它们会保留其默认值。例如,假设具有以下签名的委托人:

public delegate int MyDelegate(out int x, ref string y);

此代码将成功编译并执行:

var nop = DelegateHelper<MyDelegate>.NoOp;

string val = "hello";
int ret = nop(out int i, ref val);

Console.WriteLine(ret); // prints "0" 
Console.WriteLine(i); // prints "0"
Console.WriteLine(val); // unchanged, prints "hello"

现在这可能有点矫kill过正,但这应该可以回答您的问题。或者,您可以将null视为无操作哨兵值,而使用whatever?.Invoke()