以下测试通过,但我想知道是否可以进行统一治疗。可以吗?
public abstract class MyInvokable<TResult> {
public abstract TResult Invoke();
}
public class IntInvokable: MyInvokable<int> {
public override int Invoke() {
return 12;
}
}
[Test()]
public void FunctionInvokeTest () {
Func<int> foo = () => 6;
IntInvokable bar = new IntInvokable();
int six = foo.Invoke();
int twelve = bar.Invoke();
Assert.AreEqual(6, six);
Assert.AreEqual(12, twelve);
/* Now, what I really want to do but can't, as far as I can tell:
List<SomeType> list = new List<SomeType>(){foo, bar};
Assert.AreEqual(6, list[0].Invoke());
Assert.AreEqual(12, list[1].Invoke()); */
}
编辑:在这里向微软发起了一项功能请求: http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/10185579-create-an-iinvokable-t-interface
答案 0 :(得分:2)
C#不允许任何远程接近这种动态行为的东西。
但是,你确实需要走这条路,你总是可以使用dynamic
,基本上告诉编译器所有的赌注都是关闭的。这可行:
List<dynamic> list = new List<dynamic>(){foo, bar};
Assert.AreEqual(6, list[0].Invoke());
Assert.AreEqual(12, list[1].Invoke());
但您无法以任何方式保证list
变量中的任何内容实际上都有Invoke
方法。
与此类似的功能是TypeScript的结构接口 - 只要类型具有接口所需的所有方法/属性,就可以考虑实现接口。 在C#中,两个不同的东西可被视为具有相似类型的唯一方法是让它们显式声明它们实现相同的接口,或者继承相同的基类。
能够以某种方式在C#中使用结构语义会很好,但它甚至不在路线图上,AFAIK。
答案 1 :(得分:2)
要让List<T>
所需的唯一要求是T
具有无参数Invoke
方法,您需要将该要求烘焙到接口或基类中。
所以这两个中的任何一个都可行:
public interface IInvokable
{
int Invoke();
}
var l = new List<IInvokable>();
或者这个:
public class Invokable
{
public virtual int Invoke() { ... }
}
var l = new List<Invokable>();
显然,现在需要将委托包装在该类中,例如:
public class FuncInvokable : IInvokable
{
private readonly Func<int> _Func;
public FuncInvokable(Func<int> func) { _Func = func; }
public int Invoke() { return _Func(); }
}
或...您可以将委托放入列表中,并将所有对象包装在委托中:
var l = new List<Func<int>>();
l.Add(foo);
l.Add(() => bar.Invoke());
但无法定义:
var l = new List<Type that has an Invoke method>();