我有一个通用类,例如
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));
}
...
答案 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()
。