【问题标题】:Create dynamic instance from a dictionary of types [duplicate]从类型字典创建动态实例[重复]
【发布时间】:2021-12-07 04:18:56
【问题描述】:

我正在尝试根据给定的输入生成子类的实例,因此我创建了一个以 System.Type 作为键的字典(尽管我将使用字符串创建一个示例以便于理解)并返回一个 System.Type 作为值。

类似这样的:

Dictionary<string, System.Type> types = new Dictionary<string, System.Type>()
    {
        { "Weapon", System.Type.GetType("WeaponClass") },
        { "Consumable", System.Type.GetType("ConsumableClass") },
        { "Resource", System.Type.GetType("ResourceClass") }
    };

WeaponClass、ConsumableClass 和 ResourceClass 是同一类 ItemClass 的子类。

所以我想创建一个函数来做这样的事情:

public ItemClass CreateItem(string itemName)
{
    System.Type type = types[itemName];
    // This is the part that I don't know how to make
    return new type();
}

这应该返回一个对应子类的实例,但是我不知道怎么做。

谁能帮帮我?

【问题讨论】:

  • 你可以使用类似 `switch(type) { case "Weapon": { return new WeaponClass(); } case "Consumable": { return new ConsumableClass(); } } `
  • 我想你正在寻找这个:How to create a new Object instance from a Type
  • 我目前有一个类似的实现,但我宁愿有一个字典,因为它会使代码更清晰,更容易升级。不过,这是个好主意
  • 在游戏中你最不想做的就是通过反射使用动态创建
  • @JavierRieraChirivella a Dictionary 和 switch 实际上或多或少是等价的,除了在字典中您可以在运行时动态添加元素 ....

标签: c# unity3d types


【解决方案1】:

您可以使用Activator.CreateInstance 创建特定类型的新对象:

var type = types[itemName];
var item = Activator.CreateInstance(type);

但是,我建议采用稍微不同的方法。而不是包含类型的字典,而是将其设为Funcs 的列表,为您提供实际类型。它是类型安全的并且具有编译时安全性。例如:

var types = new Dictionary<string, Func<ItemClass>>()
{
    { "Weapon", () => new WeaponClass() },
    { "Consumable", () => new ConsumableClass() },
    { "Resource", () => new ResourceClass() }
};

现在你的方法可以是:

public ItemClass CreateItem(string itemName)
{
    if(types.TryGetValue(nameOfType, out var factory))
    {
        return factory();
    }

    throw new Exception("Er, no idea what that type is");
}

【讨论】:

  • 这看起来真不错!如果它适用于我的用例,我将尝试并将其标记为答案。谢谢!
  • 我只想添加你需要转换的激活器,因为它返回一个对象,所以var item = (ItemClass)Activator.CreateInstance(type); 和@OP 你应该使用typeof(WeaponClass) 等而不是GetType(string) ...否则你可以如果移动到不同的组件会遇到麻烦;)但是,由于许多原因,底部的工厂要好得多……类型安全等,但您也可以添加参数而不会陷入反射地狱;)
【解决方案2】:

根据您的情况,您可能会考虑依赖注入。有一个整洁的小库,叫做“Ninject”,我喜欢使用它。基本上,它是您的字典的功能丰富的包装器。你有一个所谓的内核,它用它们的实现来注册类型。在您可以定义的模块中,您可以将实现映射到它们各自的类型:

public class DependencyInjectionModule : NinjectModule
{
    public override void Load()
    {
        Bind<IUserInterface>().To<UserInterface>();
        Bind<IMainForm>().To<MainForm>();
        Bind<ISplashForm>().To<SplashForm>();
        Bind<ISplashScreen>().To<SplashScreen>();
    }
}

然后你去从你之前创建的每个模块中创建一个内核:

var kernel = new StandardKernel(new DependencyInjectionModule());

在此之后,使用它真的很不错 - 你可以从存储它的任何地方获取内核,然后像这样:

var userInterface = kernel.Get<IUserInterface>();
var mainForm = kernel.Get<IMainForm>();
var splashForm = kernel.Get<ISplashForm>();
var splashScreen = kernel.Get<ISplashScreen>();

然后您从程序集中获得所需的实例,该程序集甚至不必引用任何交付实现的项目。

检查一下: Ninject

【讨论】:

  • 如何从用户输入的字符串转换为相应类型的实例,这正是 OP 在这里所要求的?
  • @derHugo 你能进一步澄清这个问题吗?
  • OP 询问“我如何将string 作为输入并返回相应类型的实例”...您的回答如何解决这个问题?
  • 我在这里同意@derHugo,这个答案甚至没有接近解决 OP 的问题
  • 我指出了 OP 如何使用替代方法映射类型的方面。这就是我对这个问题的贡献。无论 OP 的情况如何,这可能是一个选项,带或不带字符串。如果您使用字符串,则可以将其放入您映射的接口中。然后,如果您加载任何实例,您可以在那里检查名称。
猜你喜欢
  • 2017-04-17
  • 2015-07-18
  • 1970-01-01
  • 1970-01-01
  • 2010-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多