C# .NET7游戏开发:无反射按类型名自动调用构造方法(适配Native AOT)
无反射实现Native AOT兼容的资产类型自动映射方案
针对.NET 7 Native AOT环境下无法使用反射的限制,以下是几种无需手动维护庞大switch语句的自动类型映射方案:
方案1:编译时代码生成(Source Generator)
这是最适配Native AOT的方案,完全在编译阶段生成静态映射代码,无运行时开销,彻底避开反射限制。
实现步骤:
- 定义标记属性:给所有
IAsset实现类添加自定义属性标记类型ID
[AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class AssetTypeAttribute : Attribute { public string TypeId { get; } public AssetTypeAttribute(string typeId) => TypeId = typeId; }
- 编写Source Generator:创建.NET Standard类库实现
ISourceGenerator,扫描所有带标记的IAsset类,自动生成类型映射代码
[Generator] public class AssetFactoryGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { } public void Execute(GeneratorExecutionContext context) { var assetType = context.Compilation.GetTypeByMetadataName("YourNamespace.IAsset"); if (assetType == null) return; // 收集所有符合条件的IAsset实现类 var assetClasses = context.Compilation.SyntaxTrees .SelectMany(tree => tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()) .Select(classDecl => context.Compilation.GetSemanticModel(classDecl.SyntaxTree).GetDeclaredSymbol(classDecl) as INamedTypeSymbol) .Where(symbol => symbol != null && symbol.ImplementsInterface(assetType) && symbol.GetAttributes().Any(attr => attr.AttributeClass?.Name == "AssetTypeAttribute")); // 生成静态工厂类代码 var codeBuilder = new StringBuilder(@" namespace YourNamespace; public static class AssetFactory { private static readonly Dictionary<string, Func<byte[], IAsset>> _factories = new() { "); foreach (var symbol in assetClasses) { var typeIdAttr = symbol.GetAttributes().First(attr => attr.AttributeClass?.Name == "AssetTypeAttribute"); var typeId = typeIdAttr.ConstructorArguments[0].Value.ToString(); codeBuilder.AppendLine($" {{ \"{typeId}\", data => new {symbol.Name}(data) }},"); } codeBuilder.Append(@" }; public static IAsset Instantiate(string id, byte[] data) { if (_factories.TryGetValue(id, out var factory)) return factory(data); throw new ArgumentException($""Unknown asset type: {id}""); } }"); context.AddSource("AssetFactory.g.cs", codeBuilder.ToString()); } }
- 使用生成的代码:给
IAsset实现类标记属性后,编译时会自动生成AssetFactory,直接调用即可
[AssetType("Player")] public class Player : IAsset { public Player(byte[] data) { // 反序列化逻辑 } } // 调用示例 var player = AssetFactory.Instantiate("Player", dataBytes);
方案2:静态构造函数自动注册(无代码生成)
如果不想编写Source Generator,可利用静态构造函数特性,让每个IAsset类自动注册到全局静态字典,Native AOT完全支持这种静态初始化方式。
实现代码:
public static class AssetFactory { public static readonly Dictionary<string, Func<byte[], IAsset>> Factories = new(); public static IAsset Instantiate(string id, byte[] data) { if (Factories.TryGetValue(id, out var factory)) return factory(data); throw new ArgumentException($"Unknown asset type: {id}"); } } // 可选基类简化注册逻辑 public abstract class AssetBase : IAsset { protected AssetBase(string typeId, Func<byte[], IAsset> factory) { AssetFactory.Factories.TryAdd(typeId, factory); } } // 实现类示例 public class Player : AssetBase { // 静态构造函数自动完成注册 static Player() { new Player("Player", data => new Player(data)); } // 仅用于注册的私有构造 private Player(string typeId, Func<byte[], IAsset> factory) : base(typeId, factory) { } // 实际反序列化构造 public Player(byte[] data) { // 反序列化逻辑 } }
优化补充:ModuleInitializer确保提前加载
用.NET 7+的[ModuleInitializer]特性,在游戏启动时批量触发所有IAsset类的静态构造,避免首次调用Instantiate时的延迟:
public static class AssetInitializer { [ModuleInitializer] public static void InitializeAssets() { // 新增类型只需在这里添加一行 _ = typeof(Player); _ = typeof(Vehicle); _ = typeof(Weapon); } }
内容的提问来源于stack exchange,提问作者Ma_rv
相关产品推荐
相关产品推荐

