C# 通过一个方法实例化各种继承类,而不进行反射

C# 通过一个方法实例化各种继承类,而不进行反射,c#,reflection,polymorphism,abstract-class,C#,Reflection,Polymorphism,Abstract Class,在我正在进行的一个项目中,我有一组块组成了一个基于三维体素的环境(比如Minecraft)。这些世界存储在一个外部数据文件中 此文件包含以下各项的数据: 每个街区, 它的位置,, 以及它的类型 调用LoadLevel方法时,我希望它迭代文件中每个块的数据,为每个块创建块对象的新实例。传递诸如位置之类的信息没有问题。这很简单 CreateBlock(Vector3 position) 问题在于类型。所有类型都是子类(想想抽象块,然后再想想继承抽象块属性的子类型,比如GrassBlock或Wate

在我正在进行的一个项目中,我有一组块组成了一个基于三维体素的环境(比如Minecraft)。这些世界存储在一个外部数据文件中

此文件包含以下各项的数据: 每个街区, 它的位置,, 以及它的类型

调用LoadLevel方法时,我希望它迭代文件中每个块的数据,为每个块创建块对象的新实例。传递诸如位置之类的信息没有问题。这很简单

CreateBlock(Vector3 position)
问题在于类型。所有类型都是子类(想想抽象块,然后再想想继承抽象块属性的子类型,比如GrassBlock或WaterBlock)。假设我想创建一个像“GrassBlock”这样的子类,而不是一个泛型块,我如何通过该方法使它做到这一点?我知道的唯一方法是通过反思,有人建议我远离反思。我是否可以通过泛型键入或其他方式来实现这一点


这似乎是游戏设计中的一个重要问题,但我问过的人似乎都不知道。有什么帮助吗?

如果没有反射,您可以使用带有
开关的工厂方法。假设
BlockType
是一个枚举

public static Block CreateBlock(BlockType type, Vector3 position)
{
    switch (BlockType type)
    {
    case BlockType.Grass:
        return new GrassBlock(position);
    case BlockType.Water:
        return new WaterBlock(position);
    default:
        throw new InvalidOperationException();
    }
}
但是为了有更易于维护的东西,您仍然可以使用反射,直到它被证明是一个瓶颈。在这种情况下,您可以切换到运行时代码生成

private static readonly Dictionary\u activators=new Dictionary();
公共静态块CreateBlock(类型blockType,矢量3位置)
{
Func工厂;
if(!\u激活器TryGetValue(块类型,出厂))
{
如果(!typeof(Block).IsAssignableFrom(blockType))
抛出新ArgumentException();
var posParam=Expression.Parameter(typeof(Vector3));
factory=Expression.Lambda(
新的(
blockType.GetConstructor(新[]{typeof(Vector3)}),
新[]{posParam}
),
posParam
).Compile();
_激活剂。添加(块类型,工厂);
}
返回工厂(职位);
}
此代码将在第一次请求给定类型的块时,在运行时生成工厂函数。如果需要,您可以使用
ConcurrentDictionary
使该函数线程安全


但这对你来说可能有点过分;)

在没有反射的情况下,您可以使用带有
开关的工厂方法。假设
BlockType
是一个枚举

public static Block CreateBlock(BlockType type, Vector3 position)
{
    switch (BlockType type)
    {
    case BlockType.Grass:
        return new GrassBlock(position);
    case BlockType.Water:
        return new WaterBlock(position);
    default:
        throw new InvalidOperationException();
    }
}
但是为了有更易于维护的东西,您仍然可以使用反射,直到它被证明是一个瓶颈。在这种情况下,您可以切换到运行时代码生成

private static readonly Dictionary\u activators=new Dictionary();
公共静态块CreateBlock(类型blockType,矢量3位置)
{
Func工厂;
if(!\u激活器TryGetValue(块类型,出厂))
{
如果(!typeof(Block).IsAssignableFrom(blockType))
抛出新ArgumentException();
var posParam=Expression.Parameter(typeof(Vector3));
factory=Expression.Lambda(
新的(
blockType.GetConstructor(新[]{typeof(Vector3)}),
新[]{posParam}
),
posParam
).Compile();
_激活剂。添加(块类型,工厂);
}
返回工厂(职位);
}
此代码将在第一次请求给定类型的块时,在运行时生成工厂函数。如果需要,您可以使用
ConcurrentDictionary
使该函数线程安全


但这对你来说可能有点过分;)

泛型类型仍然需要反射

首先:你要找的是工厂模式。它为您创建对象,而无需在任何地方自己显式地创建对象

基本上有两种选择:

  • 反射
这确实会对性能产生影响,但如果您还没有确定这是一个问题,请不要忽略它。它将是可读和可维护的

  • 开关

对每个选项进行硬编码,并根据传入的某种元数据(可以标识每个块的类型)创建一个新实例。这样做的好处是不使用反射,因此不会导致性能损失,但扩展性也会降低,如果有500个不同的块,您可以猜测代码的外观。

通用类型仍然需要反射

首先:你要找的是工厂模式。它为您创建对象,而无需在任何地方自己显式地创建对象

基本上有两种选择:

  • 反射
这确实会对性能产生影响,但如果您还没有确定这是一个问题,请不要忽略它。它将是可读和可维护的

  • 开关

对每个选项进行硬编码,并根据传入的某种元数据(可以标识每个块的类型)创建一个新实例。这样做的好处是不使用反射,因此不会导致性能损失,但扩展性也会降低,如果有500个不同的块,您可以猜测代码的外观。

当然,您可以创建没有反射的对象。 简单地为每个类分配整数索引:

Func<Vector3, Block>[] factories =
{
    (v) => new GrassBlock(v),    // index 0
    (v) => new WaterBlock(v),    // index 1
    . . .
}
Func[]工厂=
{
(v) =>新块(v),//索引0
(v) =>新的水锁(v),//索引1
. . .
}

将此索引保存在外部数据中。在反序列化时,读取向量3
v
和索引
i
,然后调用
var block=factories[i](v)

当然,您可以创建没有任何反射的对象。 简单地为每个类分配整数索引:

Func<Vector3, Block>[] factories =
{
    (v) => new GrassBlock(v),    // index 0
    (v) => new WaterBlock(v),    // index 1
    . . .
}