如何在C#中根据类型名称自动调用构造函数而无需使用反射?

huangapple go评论64阅读模式
英文:

How to automatically call constructor by type name without reflection in C#?

问题

我目前正在使用C#(.NET 7)从头开始编写一款游戏,并希望在没有使用反射(因为涉及到本地AOT)的情况下自动将数据映射到脚本构造函数。

我已经有一种方法来序列化和反序列化类字段本身,但当需要实际确定在读取数据块时要创建哪种对象类型时,我就有些困惑,就像Unity需要将YAML数据绑定到实体的组件时一样。

在我看来,最朴素的方法是在每个数据块的开头有一个类型ID,然后使用switch语句调用具有该ID名称的类型的构造函数,就像这样:

public static IAsset Instantiate(string id, byte[] data)
{
    switch (id)
    {
        case "Player": return new Player(data);
        case "Vehicle": return new Vehicle(data);
        case "Weapon": return new Weapon(data);
        default: throw new ArgumentException();
    }
}

如果可用的资产类型非常少,这种方式是可以的,但我预计以后会有大量不同的脚本。

是否有任何方法可以在编译时或运行时自动为每个脚本完成这项任务?

英文:

I'm currently writing a game from scratch in C# (.NET 7) and I would like to automate mapping data to script constructors without reflection (because of Native AOT).

I already have a way to serialize and de-serialize the class fields themselves, but I'm lost when it comes to actually determining which object type to create when reading in a block of data, like e.g. Unity when it has to bind YAML data to a Component as part of an Entity.

In my eyes the most naive approach would be to have a type ID at the start of each data block and a switch statement that calls the constructor of the type with that ID as the name, like this:

public static IAsset Instantiate(string id, byte[] data)
{
    switch (id)
    {
        case "Player": return new Player(data);
        case "Vehicle": return new Vehicle(data);
        case "Weapon": return new Weapon(data);
        default: throw new ArgumentException();
    }
}

This would be fine if the amount of available asset types were very low, but I'm assuming there's going to be a ton of different scripts down the line.

Is there any way this can be done automatically for every script at compile or run time?

答案1

得分: 1

你可以使用查找表(即委托的字典)。

static readonly List<string, Func<byte[], IAsset>> _masterList = new List<string, Func<byte[], IAsset>>
{
    { "Player", x => new Player(x) },
    { "Vehicle", x => new Vehicle(x) },
    { "Weapon", x => new Weapon(x) }
};

public static IAsset Instantiate(string id, byte[] data)
{
   if (!_masterList.ContainsKey(id)) throw new ArgumentException("无效的ID");
   return _masterList[id](data);
}
英文:

You could use a lookup table (i.e. a dictionary of delegates).

static readonly List&lt;string,Func&lt;byte[],IAsset&gt;&gt; _masterList = new List&lt;string,Func&lt;byte[],IAsset&gt;&gt;
{
    { &quot;Player&quot;, x =&gt; new Player(x) },
    { &quot;Vehicle&quot;, x =&gt; new Vehicle(x) },
    { &quot;Weapon&quot;, x =&gt; new Weapon(x) }
};

public static IAsset Instantiate(string id, byte[] data)
{
   if (!_masterList.ContainsKey(id)) throw new ArgumentException(&quot;Invalid ID&quot;);
   return _masterList[id](data);
}

huangapple
  • 本文由 发表于 2023年2月14日 01:25:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75439244.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定