3

我试图从泛型类中的静态字段中获取泛型类型的实例值,它抛出了这个异常:

不能对 Type.ContainsGenericParameters 为 true 的类型的字段执行后期绑定操作

public class ManagerTemplate<Q, T> : IObjectManager
        where T : Filmation.Runtime.Engine.ObjectId, new( )
        where Q : ManagerTemplate<Q, T>, new( ) {

        public readonly static Q Instance = new Q( );         <---- STATIC FIELD
}


private static void FindManagers( ) {
    var IObjectManagerType = typeof( IObjectManager );

    var managers = IObjectManagerType.Assembly
          .GetTypes( )
          .Where( t => !t.IsAbstract && t.GetInterfaces().Any( i => i == IObjectManagerType) );

    foreach (var manager in managers) {
         var fi = manager.GetField( "Instance" );
         var instance = fi.GetValue( null );                  <--- EXCEPTION
    }
}

我尝试使用 GetGenericTypeDefinition,但继续抛出异常。

我在谷歌搜索过,但我没有找到它是如何完成的......

有谁知道怎么做?

编辑:使用静态属性相同

这是我已经实现的解决方法,(尽管我想知道是否可以使用反射来完成):

public static Q Instance { get; private set; }

static ManagerTemplate( ) {
     Instance = new Q( );
     Environment.Managers.Add( Instance );
}
4

3 回答 3

3

问题是您试图从未绑定的泛型类型(即具有未指定类型参数的泛型类型)中获取 Instance 字段。无法实例化未绑定类型或调用其方法。您需要一个具有指定所有类型参数的绑定泛型类型,但请考虑每个不同的具体绑定类型不会共享静态字段。例如ManagerTemplate<Class1, Class2>将返回一个不同的 Instance ManagerTemplate<Class1, Class3>。但是,所有实例都ManagerTemplate<Class1, Class2>将共享静态字段。

您可以使用反射将类型参数绑定到未绑定泛型类型的未指定类型参数Type.MakeGenericType。您需要为 FindManagers 类提供类型参数:

private static void FindManagers<Q,T>( ) {
    var IObjectManagerType = typeof( IObjectManager );

    var managers = IObjectManagerType.Assembly
          .GetTypes( )
          .Where( t => !t.IsAbstract && t.GetInterfaces().Any( i => i == IObjectManagerType) );

    foreach (var manager in managers) {
         var concreteType = manager.MakeGenericType(typeof(Q), typeof(T));
         var fi = concreteType.GetField( "Instance" );
         var instance = fi.GetValue( null );                
    }
}
于 2013-08-10T01:12:02.987 回答
3

您无法public readonly static Q Instance = new Q( );Generic Type Definition ManagerTemplate<Q, T>中获取 的值,仅仅是因为 没有具体的类型Q

Q如果您还不知道具体类型是什么,您如何获得泛型类型定义的实例Q?很简单:你不能。

现在......如果您想要获得的是从定义ManagerTemplate<Q, T>泛型类型参数的位置下降的类型的实例Q,那么您实际上想要从搜索中排除泛型类型参数。

private static IEnumerable<IObjectManager> FindManagers()
{
  Type type = typeof(IObjectManager);
  IEnumerable<Type> managers = type.Assembly
                   .GetTypes()
                   .Where(t => !t.IsAbstract && t.GetInterfaces().Contains(type));

  foreach (Type manager in managers)
  {
    var fi = manager.GetField("Instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
    if (fi != null && 
       !fi.FieldType.IsGenericParameter && 
        type.IsAssignableFrom(fi.FieldType))
    {
      yield return (IObjectManager) fi.GetValue(null);
    }
  }
}

ManagerTemplate<Q, T>这将为您提供在所有定义类型的派生类中定义的所有“管理Q器”。

于 2013-08-10T01:25:07.253 回答
2

我会像下面这样解决问题。让我们从代码开始,然后我将解释为什么代码

public interface IObjectManager {
}

public abstract class ObjectManager: IObjectManager {
    static IEnumerable<IObjectManager> ManagerInstancesIterator() {
        foreach(var managerType in managerTypes) {
            var info=managerType.BaseType.GetField("Instance");
            var instance=info.GetValue(null) as IObjectManager;
            yield return instance;
        }
    }

    public static IObjectManager[] FindManagerInstances() {
        return ManagerInstancesIterator().ToArray();
    }

    public ObjectManager() {
        managerTypes.Add(this.GetType());
    }

    static readonly HashSet<Type> managerTypes=new HashSet<Type>();
}

public class ManagerTemplate<Q, T>: ObjectManager
    where T: new()
    where Q: ManagerTemplate<Q, T>, new() {
    public readonly static Q Instance=new Q();
}

public class CuriousClass<T>
    : ManagerTemplate<CuriousClass<T>, T> where T: new() {
}

测试代码:

public static partial class TestClass {
    public static void TestMethod() {
        var instanceByObject=CuriousClass<object>.Instance;
        var instanceByInt32=CuriousClass<int>.Instance;

        var instances=ObjectManager.FindManagerInstances();
    }
}

注意 的约束Filmation.Runtime.Engine.ObjectId被暂时删除,您可以根据需要将其添加回来。

您正在使用奇怪的重复模板模式,而消费者的代码实际上无法在ManagerTemplate<Q, T>不实现继承自的具体类的情况下进行实例化ManagerTemplate<Q, T>

由于您遇到异常的原因,其他答案指出了这一点。该类ManagerTemplate<Q, T>是一个开放的泛型类,也就是说,除非您指定类型参数,否则泛型类只是一个定义;它没有类型实例。

关闭的泛型类型将位于运行时类型缓存中,并且不存在于程序集中。因此,获取真正用于实例化对象的类型实例的最简单方法是将它们存储在集合中。但是作为ManagerTemplate<Q, T>泛型,如果我们在其类声明中声明一个集合,那么每个关闭的泛型类型都会有不同的集合。这就是为什么要有基类的原因ObjectManager。但是,我们不希望自己被实例化,所以它是抽象的。

为什么managerTypes.Add(this.GetType());是在实例构造函数中而不是类初始化器中,原因很简单,静态构造函数不会让我们知道this是什么类型。

所以最后,我认为这种设计是一种实用的解决方案。

于 2013-08-10T02:41:13.673 回答