简体   繁体   English

通用构造函数和反射

[英]Generic constructors and reflection

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. 属性'ContainsGenericParameters'返回两个构造函数false。 Is there any way to find out that constructors[0] is the generic one? 有没有办法找出构造函数[0]是通用的? They both have the same signature, but I would like to call the "real" string one. 它们都有相同的签名,但我想称之为“真正的”字符串。

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. 我的代码也应该这样。

You want System.Reflection.ParameterInfo.ParameterType.IsGenericParameter. 您需要System.Reflection.ParameterInfo.ParameterType.IsGenericParameter。 Here's a VS2008 unit test that passes that illustrates this: 这是一个通过的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. 不完全确定你的意图是什么。

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. 您将所有参数视为字符串的原因是因为您在获取构造函数之前显式绑定了类型Foo。 Try switching your code to the following which uses an unbound Foo and hence will return generic parameters in the methods. 尝试将代码切换为使用未绑定Foo的以下代码,因此将返回方法中的泛型参数。

var constructors = typeof( Foo<> ).GetConstructors();

You could check the Type.GetGenericArguments result 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)). 只需调用类型不一样的类型(类型!= typeof(T))。

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. 这条路径上的各种选项都是可能的,因此您可以避免必须反映该类的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM