根据 Class<T> 的类型创建泛型类的实现实例

Posted

技术标签:

【中文标题】根据 Class<T> 的类型创建泛型类的实现实例【英文标题】:Create instance of implementation of generic class based on type of Class<T> 【发布时间】:2021-11-28 18:06:51 【问题描述】:

我有以下班级布局:

abstract class GenericClass<TArgs> where TArgs : ArgsBaseClass  
abstract class ArgsBaseClass  

class RandomArgs : ArgsBaseClass  
class RandomClass : GenericClass<RandomArgs>  

现在我希望能够从RandomArgs 的实例创建RandomClass 的实例,而不知道它应该是RandomClass。 IE。不知何故,我需要推导出 RandomClass 实现 GenericClass&lt;RandomArgs&gt; 的事实。

我可以使用以下方法创建GenericClass&lt;RandomArgs&gt; 的实例:

void CreateSpecificInstance(ArgsBaseClass args)

    Type genericType = typeof(GenericClass<>).MakeGenericType(typeof(args));
    var genericInstance = Activator.CreateInstance(genericType, typeof(args));
    // But then I need help for the following step:
    Type specificType = ...  // in this case RandomClass, but should be derived from 'args'.
    var specificInstance = (specificType)genericInstance;

感谢任何帮助。


编辑:

基本上我有多个args 类。每个args 类都有一个相应的“正常”类,可以用这个args 类进行初始化。因此GenericClass&lt;ArgsBaseClass&gt;

现在我希望能够在将ArgsBaseClass 的实例传递给某个函数时创建“普通”类的实例(在我的示例中名为CreateSpecificInstance

根据我的 cmets,我想可以在每个 args 类和“普通”类之间创建一个映射,例如使用字典,但每当您添加新的 args + 'normal' 类组合时,您还需要更新此字典。因此我猜这里应该使用使用反射的解决方案,但是我没有看到使用反射的解决方案不会迭代程序集或命名空间中的所有类,因此我正在寻求帮助。

【问题讨论】:

你应该解释为什么你需要这个。 您必须在所有加载的程序集中搜索这种类型。如果有多种这样的类型,你会怎么做? 在编译时还不清楚您知道哪些类型。你知道args 的确切类型吗,你能把TArgs args) where TArgs : ArgsBaseClass 设为通用吗? 请查看 xyproblem.info。正如@aybe 评论的那样,请解释您想要实现的目标,而不是仅仅询问您正在尝试的问题。 伙计们,这个要求是有道理的。我怀疑如果他问他需要一种“注册通用实现类的动态方式”,那么这个问题就会过于宽泛。 【参考方案1】:

回答这个问题最重要的是很多非常具体的细节,特别是你的实现。

所以,这个答案不是一个具体的解决方案,而是旨在描述一组解决方案,或者至少是解决方案的成分。

在这类问题中你可能遇到的最困难的事情是接口和类型的动态发现。例如。您在编译时不知道 Args 类型和 GenericClass 实现的完整列表。

如果你事先知道,你很幸运。只需加载一个Dictionary&lt;Type, Type&gt;,其中Args 类型作为键,实现类型作为值。

如果不这样做,您还有一个额外的问题需要解决:如何唯一标识实现类型?也有很多方法可以解决这个问题,但它们涉及某种形式的 Assembly.GetTypes() 或(更好)使用具有内置程序集扫描功能的良好 IoC 框架(我最喜欢 Autofac)。如果您找到相同 args 的两个实现,您还需要解决问题(可能只是原则上,但这是您需要回答的问题)。

回到我们的实现类型。我们有一个Args 实例或类型,我们有我们的类型字典,所以我们找到了GenericClass&lt;TArgs&gt; 的正确实现的Type。现在您需要一种构建实例的方法。如果你可以确保你有一个无参数的构造函数,你可以调用Activator.CreateInstance(myGenericType)。这将为您提供一个 RandomClass 的实例,装在 object 内。

您需要以某种方式投射它,才能使用它,也许是GenericClass&lt;RandomArgs&gt;

如果您没有无参数构造函数,并且可以,请尝试使用 IoC 容器来为您发现构造函数依赖项进行艰苦的工作。同样,Autofac 是一个不错的候选者,但有很多选择,这是一个很深的兔子洞。

所以,在最简单的情况下,您只需要这样的东西:


private Dictionary<Type, Type> _implementations =
    new Dictionary<Type, Type>();

public void RegisterImplementation<TArgs, TImpl>()
    where TArgs : ArgsBaseClass
    where TImpl : GenericClass<TArgs>, new()

    _implementations[typeof(TArgs)] = typeof(TImpl);


public GenericClass<TArgs> MakeClass<TArgs>(TArgs args) where TArgs : ArgsBaseClass

    var implementationType = _implementations.TryGetValue(typeof(TArgs), out var t)
        ? t
        : throw new ArgumentException("The args type " + typeof(TArgs) + " is unrecognized.", nameof(args));

    return (GenericClass<TArgs>)Activator.CreateInstance(implementationType);

【讨论】:

感谢您的详尽回答,但我想我现在将另辟蹊径,并将其作为答案。【参考方案2】:

我决定另辟蹊径,改用Factory Method Pattern。

【讨论】:

以上是关于根据 Class<T> 的类型创建泛型类的实现实例的主要内容,如果未能解决你的问题,请参考以下文章

java,关于利用反射自动设置List<T>中T的类型

C#如何将类型Type作为泛型T的参数T传递

java中Class<T>类型和不写<T>的区别

如何根据T创建不同行为的泛型方法?

无法分配泛型类型的值

java中的 class<T>和 class<?>类型 有啥区别,可以互相转换来用吗?是好举例来说明一下