You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现基于抽象类继承与指定属性值的实例工厂?

实现符合要求的命令工厂类

我来帮你完成这个工厂类的实现,核心思路是通过反射扫描程序集中符合条件的类型(继承自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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:32:43