C# 泛型构造函数和反射
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/869395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generic constructors and reflection
提问by tanascius
Is it possible to see which constructor was the generic one?
是否可以看到哪个构造函数是通用构造函数?
internal class Foo<T>
{
public Foo( T value ) {}
public Foo( string value ) {}
}
var constructors = typeof( Foo<string> ).GetConstructors();
The property 'ContainsGenericParameters' returns me for both constructors false. Is there any way to find out that constructors[0] is the generic one? They both have the same signature, but I would like to call the "real" string one.
属性 'ContainsGenericParameters' 为两个构造函数返回 false。有没有办法找出构造函数[0]是通用的?它们都具有相同的签名,但我想称其为“真实”字符串。
EDIT:
编辑:
I want to invoke the given type using
我想使用调用给定类型
ilGen.Emit( OpCodes.Newobj, constructorInfo );
so I need to work with the bound version. But I would like to invoke the "best" constructor. That should be the standard behaviour. When I call
所以我需要使用绑定版本。但我想调用“最佳”构造函数。这应该是标准行为。当我打电话
new Foo<string>()
the constructor with the string-signature (and not the one with the generic signature) is called. The same should happen with my code.
调用具有字符串签名(而不是具有通用签名的构造函数)的构造函数。我的代码也应该如此。
采纳答案by Colin Burnett
You want System.Reflection.ParameterInfo.ParameterType.IsGenericParameter. Here's a VS2008 unit test that passes that illustrates this:
您需要 System.Reflection.ParameterInfo.ParameterType.IsGenericParameter。这是一个通过的 VS2008 单元测试,说明了这一点:
Class:
班级:
public class Foo<T>
{
public Foo(T val)
{
this.Value = val.ToString();
}
public Foo(string val)
{
this.Value = "--" + val + "--";
}
public string Value { get; set; }
}
Test method:
测试方法:
Foo<string> f = new Foo<string>("hello");
Assert.AreEqual("--hello--", f.Value);
Foo<int> g = new Foo<int>(10);
Assert.AreEqual("10", g.Value);
Type t = typeof(Foo<string>);
t = t.GetGenericTypeDefinition();
Assert.AreEqual(2, t.GetConstructors().Length);
System.Reflection.ConstructorInfo c = t.GetConstructors()[0];
System.Reflection.ParameterInfo[] parms = c.GetParameters();
Assert.AreEqual(1, parms.Length);
Assert.IsTrue(parms[0].ParameterType.IsGenericParameter);
c = t.GetConstructors()[1];
parms = c.GetParameters();
Assert.AreEqual(1, parms.Length);
Assert.IsFalse(parms[0].ParameterType.IsGenericParameter);
The notable point here is the parms[0].ParameterType.IsGenericParameter check which checks if the parameter is a generic or not.
这里值得注意的一点是 parms[0].ParameterType.IsGenericParameter 检查,它检查参数是否为泛型。
Once you've found your constructor then you've got the ConstructorInfo to pass to Emit.
找到构造函数后,您就可以将 ConstructorInfo 传递给 Emit。
public System.Reflection.ConstructorInfo FindStringConstructor(Type t)
{
Type t2 = t.GetGenericTypeDefinition();
System.Reflection.ConstructorInfo[] cs = t2.GetConstructors();
for (int i = 0; i < cs.Length; i++)
{
if (cs[i].GetParameters()[0].ParameterType == typeof(string))
{
return t.GetConstructors()[i];
}
}
return null;
}
Not exactly sure what your intention is though.
不完全确定你的意图是什么。
回答by Reed Copsey
You could check the Type.GetGenericArgumentsresult type(s), and compare that to the constructor parameter type.
您可以检查Type.GetGenericArguments结果类型,并将其与构造函数参数类型进行比较。
Just call the one with a type that is not the same (type != typeof(T)).
只需调用类型不同的那个(type != typeof(T))。
回答by JaredPar
Slight clarification. Neither of the constructors are generic methods. They are normal methods on a generic class. For a method to be "generic" it must have a generic parameter . So doing a test like "IsGenericMethod" will return false.
稍微澄清一下。这两个构造函数都不是泛型方法。它们是泛型类的普通方法。对于“通用”的方法,它必须有一个通用参数。所以做一个像“IsGenericMethod”这样的测试将返回false。
It's also not easy to simply look at the parameters and determine if they are generic. For the sample you gave it's possible to walk the arguments and look for a generic parameter. But also consider the following code
简单地查看参数并确定它们是否是通用的也不容易。对于您提供的示例,可以遍历参数并查找通用参数。但也要考虑下面的代码
public Foo(IEnumerable<T> p1) ...
public Foo(IEnumerable<KeyValuePair<string,Func<T>>> p1) ...
You'll need to take items like this into account.
你需要考虑这样的项目。
EDIT
编辑
The reason you're seeing all arguments as string is because you explicitly bound the type Foo before getting the constructors. Try switching your code to the following which uses an unbound Foo and hence will return generic parameters in the methods.
您将所有参数视为字符串的原因是因为您在获取构造函数之前显式绑定了类型 Foo。尝试将您的代码切换到以下使用未绑定 Foo 的代码,因此将在方法中返回通用参数。
var constructors = typeof( Foo<> ).GetConstructors();
回答by nkirkes
Can you explain a bit more what you're trying to accomplish, when you say you want to call the concrete constructor? I'm just curious if there's another way to solve your issue without having to detect whether the constructor contains generic parameters.
当你说你想调用具体的构造函数时,你能解释一下你想要完成的事情吗?我只是好奇是否有另一种方法可以解决您的问题,而不必检测构造函数是否包含通用参数。
I'm thinking chaining constructors or building logic into the generic one to behave a certain way if the parameter passed in is a string, such as:
如果传入的参数是字符串,我正在考虑将构造函数或构建逻辑链接到通用构造函数中以某种方式运行,例如:
static void Main(string[] args)
{
Console.WriteLine(new Foo<string>("myValue").IsValueAString);
Console.WriteLine(new Foo<int>(1).IsValueAString);
Console.ReadLine();
}
public class Foo<T>
{
public bool IsValueAString = false;
public Foo(T value) {
if (value is string)
{
IsValueAString = true;
}
}
}
Another option would be to create a concrete implementation of Foo, something like:
另一种选择是创建 Foo 的具体实现,例如:
internal class Foo<T>
{
...
}
internal class MyFoo : Foo<string>
{
...
}
and embed any specific logic in the constructor of the descendant. All kinds of options down this path are possible so you can avoid having to reflect the info out of that one class.
并在后代的构造函数中嵌入任何特定的逻辑。沿着这条路径的各种选项都是可能的,因此您可以避免必须反映该类之外的信息。