从fieldinfo获取泛型值引发异常:“;无法执行后期边界操作&”;

本文关键字:执行 边界 操作 泛型 获取 fieldinfo 异常 | 更新日期: 2023-09-27 18:27:01

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

不能对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 );
}

从fieldinfo获取泛型值引发异常:“;无法执行后期边界操作&”;

不能从泛型类型定义ManagerTemplate<Q, T>中获取public readonly static Q Instance = new Q( );的值,因为Q没有具体类型。

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

现在。。。如果您想要得到的是从定义了泛型类型参数QManagerTemplate<Q, T>派生而来的类型的实例,那么您实际上想要从搜索中排除泛型类型参数。

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>派生的所有类中定义的所有"Manager",这些类定义了Q的类型

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

我会按照以下方式解决问题。让我们从代码开始,然后我将解释为什么代码

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>是泛型的,如果我们在其类声明中声明一个集合,那么每个闭合泛型类型都会有不同的集合。这就是为什么一个基类CCD_ 19。但是,我们不希望自己被实例化,所以它是抽象的。

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

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