在运行时创建一个未知的泛型类型的C#实现

19
我有一个泛型类,可能需要在自己的方法中使用反射创建另一种泛型的实例。这很重要,因为这个Repository将T映射到数据库表中[我正在编写一个ORMish],如果表示T的类有一个表示另一个表的集合,我需要能够实例化它并将其传递给Repository [就像《盗梦空间》一样]。
如果提供该方法可以更容易地看到问题。
private PropertiesAttributesAndRelatedClasses GetPropertyAndAttributesCollection() 
{
  // Returns a List of PropertyAndAttributes

  var type = typeof(T);
  //For type T return an array of PropertyInfo

  PropertiesAttributesAndRelatedClasses PAA = new PropertiesAttributesAndRelatedClasses();
  //Get our container ready

  //Let's loop through all the properties.
  PropertyAndAttributes _paa;
  foreach(PropertyInfo Property in type.GetProperties())
  {
    //Create a new instance each time.
    _paa = new PropertyAndAttributes();

    //Adds the property and generates an internal collection of attributes for it too
    _paa.AddProperty(Property);

    bool MapPropertyAndAttribute = true;
    //This is a class we need to map to another table
    if (Property.PropertyType.Namespace == "System.Collections.Generic")
    {
      PAA.AddRelatedClass(Property);
      //var x = Activator.CreateInstance("GenericRepository", Property.GetType().ToString());
    }
    else 
    {
      foreach(var attr in _paa.Attrs) 
      {
        if (attr is IgnoreProperty)
        {
          //If we find this attribute it is an override and we ignore this property.
          MapPropertyAndAttribute = false;
          break;
        }
      }
    }
    //Add this to the list.
    if (MapPropertyAndAttribute) PAA.AddPaa(_paa);
  }
  return PAA;
}

假设有一个GenericRepository<T>,我想创建一个通过反射从属性中获得的string类型的GenericRepository<string>,我应该怎么做?我需要替换的代码行是:

//var x = Activator.CreateInstance("GenericRepository", Property.GetType().ToString());

感谢您的选择。

你的C#代码中,类型为System.Collections.Generic的属性是如何声明的?它的类型参数 <T> 是否与拥有该属性的 GenericRepository<T> 的类型参数相同? - Sergey Kalinichenko
不,基本上它只是一个类的属性的通用集合,即教师类具有班级类的列表类。存储库获取教师类并必须处理班级类,但由于它确实正在获取T,因此它必须使用反射来确定它必须处理什么。 - Jordan
那么在Teacher类中声明的属性是List<Class> Classes {/*getter and/or setter*/}吗?那么Activator.CreateInstance(Property.GetType())就可以工作了吗? - Sergey Kalinichenko
啊,但我需要一个类型为T的存储库[我的通用存储库],其中T = Classes,如果当前实例化的存储库是T = Teachers。你建议的方法可以工作,但只是为了创建类型Classes。 - Jordan
以下两个答案都是正确的。它们对你有用吗? - Sergey Kalinichenko
是的,已经测试过并且两个都有效。我给了两个赞,但最佳答案给了更易懂的那一个。非常感谢你们俩。 - Jordan
2个回答

45

我想你正在寻找MakeGenericType方法:

// Assuming that Property.PropertyType is something like List<T>
Type elementType = Property.PropertyType.GetGenericArguments()[0];
Type repositoryType = typeof(GenericRepository<>).MakeGenericType(elementType);
var repository = Activator.CreateInstance(repositoryType);

如果可以的话,我遇到了一个问题。我无法访问给定存储库的任何方法。 如果我通过立即窗口访问它,可能会说GenericRepository<Locations>,然后我执行 ?((GenericRepository<DocerZocer.Models.Location>)repository).GetAll() 这个方法可以正常工作,如何在代码中正确地进行强制转换,以便我不需要这样做?显然,我只能在立即窗口中执行此操作,因为我知道它所代表的类型。 - Jordan
@Jordan,你可以创建一个非泛型的IRepository接口,并在GenericRepository<T>类中显式实现一个返回对象数组的GetAll方法。或者你可以使用反射动态调用该方法,但速度会较慢... - Thomas Levesque

4
Activator.CreateInstance(typeof(GenericRepository<>).MakeGenericType(new Type[] { Property.GetTYpe() }))

2
这将创建一个 GenericRepository<RuntimePropertyInfo> 的实例,因为 Property.GetType() 返回 RuntimePropertyInfo... - Thomas Levesque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接