我想知道C#(或底层的.NET框架)是否支持某种“泛型委托实例”:这是一个委托实例仍然有一个未解析的类型参数,要在调用委托时解析(不是在创建代表时)。我怀疑这是不可能的,但无论如何我都在问它......
这是我想要做的一个例子,有些“???”插入C#语法似乎无法满足我想要的地方。 (显然这段代码不能编译)
class Foo {
public T Factory<T>(string name) {
// implementation omitted
}
}
class Test {
public void TestMethod()
{
Foo foo = new Foo();
??? magic = foo.Factory; // No type argument given here yet to Factory!
// What would the '???' be here (other than 'var' :) )?
string aString = magic<string>("name 1"); // type provided on call
int anInt = magic<int>("name 2"); // another type provided on another call
// Note the underlying calls work perfectly fine, these work, but i'd like to expose
// the generic method as a delegate.
string aString2 = foo.Factory<string>("name 1");
int anInt2 = foo.Factory<int>("name 2");
}
}
有没有办法在C#中实际做这样的事情?如果没有,那是语言的限制,还是在.NET框架中?
编辑: 我问的原因是因为我想将委托传递给另一个程序集中的函数,并且不希望要求其他程序集必须引用任何特定类型(在我的示例中为“Foo”类)。我希望弯曲标准的Func&lt;&gt;以某种方式委托,以适应“???”一部分。
答案 0 :(得分:7)
这是不可能的,因为你要求的是声明一个未闭合的泛型类型的变量(magic
)。
可以使用未闭合的泛型,但仅限于类型级别,例如:
delegate T FactoryDelegate<T>(string name);
var magicType = typeof (FactoryDelegate<>);
然后在稍后“关闭”该类型:
var stringMagic = magicType.MakeGenericType(typeof(string));
更新:说,这里有一个示例,说明如何使用上述技术来处理未闭合的方法“类型”。如果我们可以分配未闭合的类型,那么仍然没有那么优雅..:
public class UnclosedMethod
{
private readonly MethodInfo _method;
public UnclosedMethod(Type type, string method)
{
_method = type.GetMethod(method);
}
public T Invoke<T>(string name)
{
var fact = _method.MakeGenericMethod(typeof(T));
return (T)fact.Invoke(this, new object[] { name });
}
}
然后在代码中执行此操作:
var magic = new UnclosedMethod(typeof(Foo), "Factory");
var x = magic.Invoke<string>("bar");
答案 1 :(得分:0)
就像?:
Foo foo = new Foo();
string aString =
foo.GetType().GetMethod("Factory").MakeGenericMethod(string)
.Invoke(foo, new object[] { "name 1" });
int anInt =
foo.GetType().GetMethod("Factory").MakeGenericMethod(int)
.Invoke(foo, new object[] { "name 2" });
现在,如果你想使用一个委托,你可能会得到类似的东西:
public delegate T FactoryDelegate<T>(string name);
然后,您可以拨打电话:
public TestMethod1(FactoryDelegate<dynamic> factory)
{
object o = factory("name 3");
}
答案 2 :(得分:0)
一个好问题。首先,我们可以看到C#不允许您使用通用的Invoke
方法定义任何委托类型。类型参数根本没有空间。代表名称后面的列表用于代表类型本身的参数。
因此,我参加了CIL并生成了看起来像是带有通用Invoke
的委托:
.class public auto ansi sealed GenericDelegate extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed
{
}
.method public hidebysig newslot virtual instance !!T Invoke<T>(!!T arg) runtime managed
{
}
}
令我惊讶的是,C#实际上可以使用这种类型而不会出现问题–您可以从通用方法(具有匹配约束)创建这种类型的实例,然后程序可以编译。但是,结果是无效的CIL,因为构造委托使用ldftn
指令,但是泛型方法没有与之关联的可执行代码,因为它是泛型的。
即使我在ECMA-335中找不到任何明显禁止委托的内容,运行时也会拒绝它。问题是runtime
的{{1}}属性指定运行时提供此方法的实现,但是当该方法是通用方法时不支持此方法。尽管可以修改Invoke
以允许通用方法,并且在这种情况下可以提供ldftn
的实现,但事实并非如此。< / p>
但是,我同意有时这个概念很有用。尽管运行时无法为您提供帮助,但最简单的方法可能只是使用接口:
Invoke
为所需的每个参数变体创建一个接口,并为每个需要调用的方法创建一个实现。如果您还想拥有类似于class Foo
{
public T Factory<T>(string name)
{
}
}
class FooFactory : IGenericFunc<string>
{
readonly Foo target;
public FooFactory(Foo target)
{
this.target = target;
}
public T Invoke<T>(string name)
{
return target.Factory<T>(name);
}
}
interface IGenericFunc<TArg>
{
T Invoke<T>(TArg arg);
}
的功能,则很有可能必须使用Delegate.CreateDelegate
使其具有一定的性能。