首页 > 解决方案 > 如何从仅在 C# 运行时知道的字符串中实例化可用对象?

问题描述

想象一下,我正在制作一款游戏,并且我将游戏中角色的元数据存储在某种明文配置文件中:

class=Orc,hitpoints=10
class=Ghoul,hitpoints=8

类似的东西。也许是 XML。在我的代码中,我有相应的类:

class Orc
{
    int hitpoints;
...
}

我希望能够在运行时的任意时间解析这样的配置文件,并创建对象。想象一下加载保存文件的用例。我可以使用 Activator.CreateInstance 轻松创建此类对象:

// pretend we parse the enemy type from the config to string enemytype here
object Enemy = Activator.CreateInstance(Type.GetType(enemytype));

但问题是这些对象并不是真正可用的。尽管事实上

Enemy.GetType()

返回“Orc”,这些对象中的方法不可访问。

Enemy.hitpoints

抱怨“'object'不包含'hitpoints'的定义”,因为它在编译时一无所知。而且我不能只转换为正确的类型,因为如果我在编译时知道正确的类型,我就不需要以这种方式创建对象。那么如何使这些对象用作它们的实际类型呢?或者这只是解决这个问题的坏方法?我最初的目标是避免硬编码各种条件逻辑来创建这些对象,而只是从解析的字符串中即时生成东西。我能想到的另一个选项是使用一个巨大的 switch 语句来根据解析的字符串进行选择,但这感觉很糟糕。

标签: c#oop

解决方案


正如其他人已经提到的那样,如果您要做的是能够保存和加载敌人的实例,那么序列化就是要走的路。
尝试以下链接了解序列化的基本方法。

仅出于学习/实验的目的:为了使您当前的实现工作,所有敌人对象都需要实现一个接口,您可以在编译时将 Activator.CreateInstance 的结果转换为该接口。例如:

public interface IEnemy
{
    int Hitpoints { get; set; }
...
}

public class Orc : IEnemy
{
    public int Hitpoints { get; set; }
...
}

public class Ghoul : IEnemy
{
    public int Hitpoints { get; set; }
...
}

然后创建实例的调用变为:

// pretend we parse the enemy type from the config to string enemytype here
IEnemy enemy = (IEnemy)Activator.CreateInstance(Type.GetType(enemytype));

此时,您可以访问接口上指定为所有敌人通用的任何方法和属性。

需要注意的是,如果激活器创建的敌人类型没有实现 IEnemy,您的程序将抛出 InvalidCastException。

无论您的最终实现如何,几乎可以肯定的是,拥有一个 IEnemy 接口以允许抽象对象是什么类型的敌人并提供通用功能将证明是有用的。


推荐阅读