通用构造函数和反射

时间:2009-05-15 15:36:25

标签: c# generics reflection

是否可以看到哪个构造函数是通用构造函数?

internal class Foo<T>
{
  public Foo( T value ) {}
  public Foo( string value ) {}
}

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

属性'ContainsGenericParameters'返回两个构造函数false。有没有办法找出构造函数[0]是通用的?它们都有相同的签名,但我想称之为“真正的”字符串。

修改

我想使用

调用给定的类型
ilGen.Emit( OpCodes.Newobj, constructorInfo );

所以我需要使用绑定版本。但我想调用“最好的”构造函数。这应该是标准行为。当我打电话

new Foo<string>()

调用带有字符串签名的构造函数(而不是具有通用签名的构造函数)。我的代码也应该这样。

4 个答案:

答案 0 :(得分:8)

您需要System.Reflection.ParameterInfo.ParameterType.IsGenericParameter。这是一个通过的VS2008单元测试,说明了这一点:

类别:

public class Foo<T>
{
    public Foo(T val)
    {
        this.Value = val.ToString();
    }
    public Foo(string val)
    {
        this.Value = "--" + val + "--";
    }

    public string Value { get; set; }
}

测试方法:

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);

这里值得注意的是parms [0] .ParameterType.IsGenericParameter检查,它检查参数是否是通用参数。

找到构造函数后,您就可以将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;
}

不完全确定你的意图是什么。

答案 1 :(得分:2)

略作澄清。两个构造函数都不是通用方法。它们是泛型类的常规方法。对于“通用”方法,它必须具有通用参数。因此,像“IsGenericMethod”这样的测试将返回false。

简单地查看参数并确定它们是否通用也不容易。对于您提供的示例,可以遍历参数并查找泛型参数。但也要考虑以下代码

public Foo(IEnumerable<T> p1) ...
public Foo(IEnumerable<KeyValuePair<string,Func<T>>> p1) ...

您需要考虑这样的项目。

修改

您将所有参数视为字符串的原因是因为您在获取构造函数之前显式绑定了Foo类型。尝试将代码切换为使用未绑定Foo的以下代码,因此将返回方法中的泛型参数。

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

答案 2 :(得分:0)

您可以检查Type.GetGenericArguments结果类型,并将其与构造函数参数类型进行比较。

只需调用类型不同的类型(类型!= typeof(T))。

答案 3 :(得分:0)

当你说要调用具体的构造函数时,你能解释一下你想要完成的事吗?我只是好奇是否有另一种方法可以解决您的问题,而无需检测构造函数是否包含泛型参数。

我正在考虑将构造函数链接或构建逻辑到通用构造函数中,如果传入的参数是字符串,则表现为某种方式,例如:

    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;
            }
        }
    }

另一种选择是创建Foo的具体实现,例如:

internal class Foo<T>
{
    ...
}
internal class MyFoo : Foo<string>
{
    ...
}

并在子孙的构造函数中嵌入任何特定的逻辑。这条路径上的各种选项都是可能的,因此您可以避免必须反映该类的信息。