如何实现基于抽象类继承与指定属性值的实例工厂?
实现符合要求的命令工厂类
我来帮你完成这个工厂类的实现,核心思路是通过反射扫描程序集中符合条件的类型(继承自CommandBase且拥有指定Prefix值),然后创建对应的实例。下面是完整的实现方案:
第一步:编写工厂类
我们可以创建一个静态工厂类,利用静态构造函数提前扫描并缓存符合条件的命令类型,避免每次创建实例时重复反射,提升性能:
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; public static class CommandFactory { // 缓存命令类型与Prefix的映射,确保高效查询 private static readonly Dictionary<string, Type> _commandTypeMap = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase); static CommandFactory() { // 在类加载时初始化缓存 PopulateCommandTypeMap(); } private static void PopulateCommandTypeMap() { // 获取当前程序集中所有继承自CommandBase的非抽象类 var eligibleTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.IsSubclassOf(typeof(CommandBase)) && !t.IsAbstract); foreach (var type in eligibleTypes) { // 获取重写的Prefix属性 var prefixProp = type.GetProperty(nameof(CommandBase.Prefix), BindingFlags.Public | BindingFlags.Instance); if (prefixProp == null || !prefixProp.CanRead) continue; // 创建临时实例获取Prefix值(因为是实例属性) using var tempInstance = (CommandBase)Activator.CreateInstance(type); var prefixValue = tempInstance.Prefix; if (string.IsNullOrEmpty(prefixValue)) continue; // 检查是否有重复的Prefix,避免歧义 if (_commandTypeMap.ContainsKey(prefixValue)) { throw new InvalidOperationException($"Duplicate Prefix found: '{prefixValue}' is used by both {_commandTypeMap[prefixValue].Name} and {type.Name}"); } _commandTypeMap.Add(prefixValue, type); } } /// <summary> /// 根据指定Prefix创建对应的Command实例 /// </summary> /// <param name="prefix">命令前缀</param> /// <returns>CommandBase实例</returns> /// <exception cref="ArgumentNullException">prefix为空时抛出</exception> /// <exception cref="KeyNotFoundException">找不到对应Prefix的命令时抛出</exception> public static CommandBase CreateCommand(string prefix) { if (string.IsNullOrWhiteSpace(prefix)) throw new ArgumentNullException(nameof(prefix), "Prefix cannot be null or empty"); if (_commandTypeMap.TryGetValue(prefix, out var commandType)) { return (CommandBase)Activator.CreateInstance(commandType); } throw new KeyNotFoundException($"No command found with prefix '{prefix}'"); } }
第二步:测试工厂类
在你的Program类中,可以这样调用工厂来创建实例:
class Program { static void Main(string[] args) { try { // 创建PaintCommand实例 var paintCmd = CommandFactory.CreateCommand("P"); Console.WriteLine($"Created command: {paintCmd.GetType().Name}, Prefix: {paintCmd.Prefix}"); // 创建WalkCommand实例 var walkCmd = CommandFactory.CreateCommand("W"); Console.WriteLine($"Created command: {walkCmd.GetType().Name}, Prefix: {walkCmd.Prefix}"); // 测试不存在的Prefix,会抛出异常 var invalidCmd = CommandFactory.CreateCommand("X"); } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); } } }
优化建议
如果觉得创建临时实例获取Prefix不够高效,可以改用自定义特性来标记Prefix,这样不需要实例化就能读取值:
1. 定义自定义特性
[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class CommandPrefixAttribute : Attribute { public string Prefix { get; } public CommandPrefixAttribute(string prefix) { Prefix = prefix ?? throw new ArgumentNullException(nameof(prefix)); } }
2. 给命令类添加特性
[CommandPrefix("P")] public class PaintCommand : CommandBase { public override string Prefix { get; } = "P"; } [CommandPrefix("W")] public class WalkCommand : CommandBase { public override string Prefix { get; } = "W"; }
3. 修改工厂类的扫描逻辑
把PopulateCommandTypeMap方法中的Prefix获取逻辑改成读取特性:
private static void PopulateCommandTypeMap() { var eligibleTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.IsSubclassOf(typeof(CommandBase)) && !t.IsAbstract); foreach (var type in eligibleTypes) { var prefixAttr = type.GetCustomAttribute<CommandPrefixAttribute>(); if (prefixAttr == null || string.IsNullOrEmpty(prefixAttr.Prefix)) continue; var prefixValue = prefixAttr.Prefix; if (_commandTypeMap.ContainsKey(prefixValue)) { throw new InvalidOperationException($"Duplicate Prefix found: '{prefixValue}' is used by both {_commandTypeMap[prefixValue].Name} and {type.Name}"); } _commandTypeMap.Add(prefixValue, type); } }
这种方式不仅性能更好,还能让Prefix的定义更集中,避免属性和标记不一致的问题。
内容的提问来源于stack exchange,提问作者Anon Dev
相关产品推荐
相关产品推荐

