代码之家  ›  专栏  ›  技术社区  ›  tanascius

通用构造函数和反射

  •  9
  • tanascius  · 技术社区  · 15 年前

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

    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 回复  |  直到 15 年前
        1
  •  8
  •   Colin Burnett    15 年前

    您需要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检查,它检查参数是否为泛型。

    一旦找到了构造函数,就可以通过constructrinfo来发出。

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

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

        2
  •  2
  •   JaredPar    15 年前

    稍微澄清一下。两个构造函数都不是泛型方法。它们是泛型类上的普通方法。对于要成为“泛型”的方法,它必须具有泛型参数。因此,执行类似“IsGenericMethod”的测试将返回false。

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

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

    你需要考虑像这样的项目。

    编辑

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

    var constructors = typeof( Foo<> ).GetConstructors();
    
        3
  •  0
  •   Reed Copsey    15 年前

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

    只需调用类型不相同的(类型!= Type(t)。

        4
  •  0
  •   nkirkes    15 年前

    当你说你想调用具体的构造函数时,你能再解释一下你想要完成什么吗?我只是好奇是否还有其他方法可以解决您的问题,而不必检测构造函数是否包含泛型参数。

    我正在考虑将构造函数链接起来或将逻辑构建到通用构造函数中,以便在传入的参数是字符串的情况下以某种方式工作,例如:

        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>
    {
        ...
    }
    

    并在后代的构造函数中嵌入任何特定的逻辑。沿着这条路径的所有类型的选项都是可能的,因此您可以避免从那个类中反射信息。