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

基于条件选择继承类构造器的通用实现方案咨询

How to Avoid Switch Blocks for Weapon Instantiation

Great question! Dealing with expanding switch/case blocks when instantiating subclasses is a classic maintainability problem. Here are two solid patterns to solve this, keeping your code clean even as you add more weapon types:

This approach replaces the switch block with a dictionary that maps WeaponType values to factory functions. It’s fast, explicit, and easy to maintain.

Implementation Steps:

  • Create a static dictionary that pairs each WeaponType with a function that creates the corresponding weapon instance.
  • Initialize this dictionary once (e.g., in a static constructor or app startup).
  • When you need a weapon, just look up the factory function in the dictionary and execute it.

Example Code:

public static class WeaponFactory
{
    private static readonly Dictionary<WeaponType, Func<WeaponData, BasicWeapon>> _weaponCreators;

    static WeaponFactory()
    {
        _weaponCreators = new Dictionary<WeaponType, Func<WeaponData, BasicWeapon>>
        {
            { WeaponType.ProjectileWeapon, data => new ProjectileWeapon(data) },
            { WeaponType.Laser, data => new LaserWeapon(data) },
            { WeaponType.snowBall, data => new SnowballWeapon(data) }
        };
    }

    public static BasicWeapon CreateWeapon(WeaponData weaponData)
    {
        if (_weaponCreators.TryGetValue(weaponData.WeaponType, out var creator))
        {
            return creator(weaponData);
        }
        // Handle unknown weapon type (throw exception, return default, etc.)
        throw new ArgumentOutOfRangeException(nameof(weaponData.WeaponType), $"Unknown weapon type: {weaponData.WeaponType}");
    }
}

Usage:

BasicWeapon weapon = WeaponFactory.CreateWeapon(_weaponsData);

Pros:

  • Fast: No runtime reflection, just a dictionary lookup.
  • Explicit: You can see exactly which weapon types are supported at a glance.
  • Easy to debug: No hidden logic.

Cons:

  • You need to manually add new entries to the dictionary when you create a new weapon subclass.

2. Attribute + Reflection (For Auto-Registration)

If you want to avoid manually updating the factory every time you add a new weapon, you can use custom attributes and reflection to auto-discover and register weapon types at startup.

Step 1: Create a Custom Attribute

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class WeaponTypeAttribute : Attribute
{
    public WeaponType WeaponType { get; }

    public WeaponTypeAttribute(WeaponType weaponType)
    {
        WeaponType = weaponType;
    }
}

Step 2: Mark Your Weapon Subclasses

[WeaponType(WeaponType.ProjectileWeapon)]
public class ProjectileWeapon : BasicWeapon 
{ 
    public ProjectileWeapon(WeaponData weaponData) : base(weaponData) { } 
}

[WeaponType(WeaponType.Laser)]
public class LaserWeapon : BasicWeapon 
{ 
    public LaserWeapon(WeaponData weaponData) : base(weaponData) { } 
}

[WeaponType(WeaponType.snowBall)]
public class SnowballWeapon : BasicWeapon 
{ 
    public SnowballWeapon(WeaponData weaponData) : base(weaponData) { } 
}

Step 3: Build the Factory with Reflection

public static class WeaponFactory
{
    private static readonly Dictionary<WeaponType, Func<WeaponData, BasicWeapon>> _weaponCreators;

    static WeaponFactory()
    {
        _weaponCreators = new Dictionary<WeaponType, Func<WeaponData, BasicWeapon>>();
        
        // Discover all types that inherit from BasicWeapon and have the WeaponTypeAttribute
        var weaponTypes = typeof(BasicWeapon).Assembly.GetTypes()
            .Where(t => t.IsSubclassOf(typeof(BasicWeapon)) && !t.IsAbstract)
            .Select(t => new 
            { 
                Type = t, 
                Attribute = t.GetCustomAttribute<WeaponTypeAttribute>() 
            })
            .Where(x => x.Attribute != null);

        foreach (var weapon in weaponTypes)
        {
            // Get the constructor that takes WeaponData
            var constructor = weapon.Type.GetConstructor(new[] { typeof(WeaponData) });
            if (constructor == null)
            {
                throw new InvalidOperationException($"Weapon class {weapon.Type.Name} lacks a constructor taking WeaponData");
            }

            // Create a factory function for this weapon type
            Func<WeaponData, BasicWeapon> creator = data => 
                (BasicWeapon)constructor.Invoke(new object[] { data });

            _weaponCreators.Add(weapon.Attribute.WeaponType, creator);
        }
    }

    public static BasicWeapon CreateWeapon(WeaponData weaponData)
    {
        if (_weaponCreators.TryGetValue(weaponData.WeaponType, out var creator))
        {
            return creator(weaponData);
        }
        throw new ArgumentOutOfRangeException(nameof(weaponData.WeaponType), $"Unknown weapon type: {weaponData.WeaponType}");
    }
}

Pros:

  • Auto-registration: No need to update the factory when adding new weapons—just mark the class with the attribute.
  • Scalable: Perfect if you expect to add many weapon types over time.

Cons:

  • Startup overhead: Reflection takes a small amount of time during initialization (usually negligible for most games/apps).
  • Less explicit: You have to check the attributes on each class to see which type maps to which weapon.

Which One Should You Choose?

  • Start with the Dictionary-Based Factory if you don’t have too many weapon types, or if you prefer explicit control over registration.
  • Use Attribute + Reflection if you’re building a system where new weapon types will be added frequently, or if you want to decouple the factory from the weapon implementations.

Either way, you’ll eliminate the messy switch block and make your code much easier to maintain as your weapon library grows!

内容的提问来源于stack exchange,提问作者Madcode

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:35:22