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

游戏模组开发:如何优化含大量可选参数的C# Item类构造函数?

问题描述

开发游戏模组工具时,需将包含大量可选属性的自定义类型重构为C#的Item类,仅identifier为必填属性,所有类数据均来自XML文件。当前Item类的构造函数因可选参数过多过于冗长,整体结构不够紧凑。考虑过通过字典或自定义类型传递属性,但不确定这两种方案是否更优,希望获取更合适的构造函数及类结构优化方案。

原实现代码:

public class Item(
     string identifier,
     string? nameIdentifier = null,
     string? fallbackNameIdentifier = null,
     string? descriptionIdentifier = null,
     string? name = null,
     string? aliases = null,
     string? tags = null,
     Item.Categories? category = null,
     bool? allowAsExtraCargo = null,
     float? interactDistance = null,
     float? interactPriority = null,
     bool? interactThroughWalls = null,
     bool? hideConditionBar = null,
     bool? hideConditionInTooltip = null,
     bool? requireBodyInsideTrigger = null,
     bool? requireCursorInsideTrigger = null,
     bool? requireCampaignInteract = null,
     bool? focusOnSelected = null,
     float? offsetOnSelected = null,
     float? health = null,
     bool? allowSellWhenBroken = null,
     bool? indestructible = null,
     bool? damagedByExplosions = null,
     float? explosionDamageMultiplier = null,
     bool? damagedByProjectiles = null,
     bool? damagedByMeleeWeapons = null,
     bool? damagedByRepairTools = null,
     bool? damagedByMonsters = null,
     bool? fireProof = null,
     bool? waterProof = null,
     float? impactTolerance = null,
     float? onDamagedThreshold = null,
     float? sonarSize = null,
     bool? useInHealthInterface = null,
     bool? disableItemUsageWhenSelected = null,
     string? cargoContainerIdentifier = null,
     bool? useContainedSpriteColor = null,
     bool? useContainedInventoryIconColor = null,
     float? addedRepairSpeedMultiplier = null,
     float? addedPickingSpeedMultiplier = null,
     bool? cannotRepairFail = null,
     string? equipConfirmationText = null,
     bool? allowRotatingInEditor = null,
     bool? showContentsInTooltip = null,
     bool? canFlipX = null,
     bool? canFlipY = null,
     bool? isDangerous = null,
     int? maxStackSize = null,
     bool? allowDroppingOnSwap = null,
     bool? resizeHorizontal = null,
     bool? resizeVertical = null,
     string? description = null,
     string? allowedUpgrades = null,
     bool? hideInMenus = null,
     string? subcategory = null,
     bool? linkable = null,
     string? spriteColor = null,
     float? scale = null)
{
    public enum Categories
    {
        Decorative,
        Machine,
        Medical,
        Weapon,
        Diving,
        Equipment,
        Fuel,
        Electrical,
        Material,
        Alien,
        Wrecked,
        Misc
    }

    // All of Item attributes
    public required string identifier = identifier;
    public string? nameIdentifier = nameIdentifier;
    public string? fallbackNameIdentifier = fallbackNameIdentifier;
    public string? descriptionIdentifier = descriptionIdentifier;

    public string? name = name;
    public string? aliases = aliases;
    public string? tags = tags;
    public Categories? category = category;

    public bool? allowAsExtraCargo = allowAsExtraCargo;
    public float? interactDistance = interactDistance;
    public float? interactPriority = interactPriority;
    public bool? interactThroughWalls = interactThroughWalls;

    public bool? hideConditionBar = hideConditionBar;
    public bool? hideConditionInTooltip = hideConditionInTooltip;
    public bool? requireBodyInsideTrigger = requireBodyInsideTrigger;
    public bool? requireCursorInsideTrigger = requireCursorInsideTrigger;

    public bool? requireCampaignInteract = requireCampaignInteract;
    public bool? focusOnSelected = focusOnSelected;
    public float? offsetOnSelected = offsetOnSelected;
    public float? health = health;

    public bool? allowSellWhenBroken = allowSellWhenBroken;
    public bool? indestructible = indestructible;
    public bool? damagedByExplosions = damagedByExplosions;
    public float? explosionDamageMultiplier = explosionDamageMultiplier;

    public bool? damagedByProjectiles = damagedByProjectiles;
    public bool? damagedByMeleeWeapons = damagedByMeleeWeapons;
    public bool? damagedByRepairTools = damagedByRepairTools;
    public bool? damagedByMonsters = damagedByMonsters;

    public bool? fireProof = fireProof;
    public bool? waterProof = waterProof;
    public float? impactTolerance = impactTolerance;
    public float? onDamagedThreshold = onDamagedThreshold;

    public float? sonarSize = sonarSize;
    public bool? useInHealthInterface = useInHealthInterface;
    public bool? disableItemUsageWhenSelected = disableItemUsageWhenSelected;
    public string? cargoContainerIdentifier = cargoContainerIdentifier;

    public bool? useContainedSpriteColor = useContainedSpriteColor;
    public bool? useContainedInventoryIconColor = useContainedInventoryIconColor;
    public float? addedRepairSpeedMultiplier = addedRepairSpeedMultiplier;
    public float? addedPickingSpeedMultiplier = addedPickingSpeedMultiplier;

    public bool? cannotRepairFail = cannotRepairFail;
    public string? equipConfirmationText = equipConfirmationText;
    public bool? allowRotatingInEditor = allowRotatingInEditor;
    public bool? showContentsInTooltip = showContentsInTooltip;

    public bool? canFlipX = canFlipX;
    public bool? canFlipY = canFlipY;
    public bool? isDangerous = isDangerous;
    public int? maxStackSize = maxStackSize;

    public bool? allowDroppingOnSwap = allowDroppingOnSwap;
    public bool? resizeHorizontal = resizeHorizontal;
    public bool? resizeVertical = resizeVertical;
    public string? description = description;

    public string? allowedUpgrades = allowedUpgrades;
    public bool? hideInMenus = hideInMenus;
    public string? subcategory = subcategory;
    public bool? linkable = linkable;

    public string? spriteColor = spriteColor;
    public float? scale = scale;

    public List<XmlElement>? children = null;

    /// <summary>
    /// Gets this Item represent
    /// </summary>
    /// <returns>XmlElement represent of the Item</returns>
    public XmlElement GetAsXml()
    {
        XmlDocument xmlDoc = new();
        XmlElement newItem = xmlDoc.CreateElement("Item");

        foreach(var attr in this.GetType().GetProperties())
        {
            if (attr.GetValue(this, null) != null)
            {
                newItem.SetAttribute(attr.Name.ToLower(), value: attr.GetValue(this, null).ToString());
            }
        }

        newItem.InnerText = "";

        if (this.children != null)
        {
            foreach (var child in this.children)
            {
                newItem.AppendChild(child);
            }
        }

        return newItem;
    }
}
优化方案

针对你的场景,推荐以下几种更简洁、可维护的方案:

1. 构建器模式(Builder Pattern)

这是处理大量可选参数最常用的方案,可读性和扩展性极强,还能强制保证必填参数不缺失。

实现示例

// 将枚举移出Item类,符合C#编码规范
public enum ItemCategory
{
    Decorative,
    Machine,
    Medical,
    Weapon,
    Diving,
    Equipment,
    Fuel,
    Electrical,
    Material,
    Alien,
    Wrecked,
    Misc
}

public class Item
{
    // 必填属性,用init保证实例创建后不可修改
    public required string Identifier { get; init; }

    // 所有可选属性,统一用自动属性封装
    public string? NameIdentifier { get; init; }
    public string? FallbackNameIdentifier { get; init; }
    public string? DescriptionIdentifier { get; init; }
    public string? Name { get; init; }
    public string? Aliases { get; init; }
    public string? Tags { get; init; }
    public ItemCategory? Category { get; init; }
    public bool? AllowAsExtraCargo { get; init; }
    public float? InteractDistance { get; init; }
    // ... 省略其余重复属性
    public float? Scale { get; init; }
    public List<XmlElement>? Children { get; init; }

    // 私有构造函数,仅允许通过构建器创建实例
    private Item(ItemBuilder builder)
    {
        Identifier = builder.Identifier;
        NameIdentifier = builder.NameIdentifier;
        FallbackNameIdentifier = builder.FallbackNameIdentifier;
        // ... 为所有属性赋值
        Scale = builder.Scale;
        Children = builder.Children;
    }

    // 嵌套构建器类
    public class ItemBuilder
    {
        // 必填参数在构建器构造函数中强制传入
        public string Identifier { get; }

        // 可选参数默认值为null
        public string? NameIdentifier { get; set; }
        public string? FallbackNameIdentifier { get; set; }
        public string? DescriptionIdentifier { get; set; }
        public string? Name { get; set; }
        // ... 省略其余重复属性
        public float? Scale { get; set; }
        public List<XmlElement>? Children { get; set; }

        public ItemBuilder(string identifier)
        {
            Identifier = identifier;
        }

        // 链式调用的属性设置方法,每个可选属性对应一个
        public ItemBuilder WithNameIdentifier(string? nameIdentifier)
        {
            NameIdentifier = nameIdentifier;
            return this;
        }

        public ItemBuilder WithCategory(ItemCategory? category)
        {
            Category = category;
            return this;
        }

        // ... 省略其余属性的WithXXX方法

        public ItemBuilder WithScale(float? scale)
        {
            Scale = scale;
            return this;
        }

        // 最终构建Item实例的方法
        public Item Build()
        {
            return new Item(this);
        }
    }

    public XmlElement GetAsXml()
    {
        XmlDocument xmlDoc = new();
        XmlElement newItem = xmlDoc.CreateElement("Item");

        foreach(var prop in this.GetType().GetProperties())
        {
            var value = prop.GetValue(this);
            if (value != null)
            {
                newItem.SetAttribute(prop.Name.ToLower(), value.ToString());
            }
        }

        if (Children != null)
        {
            foreach (var child in Children)
            {
                newItem.AppendChild(xmlDoc.ImportNode(child, true));
            }
        }

        return newItem;
    }
}

使用方式

// 仅需设置必填项和需要的可选项,代码可读性极强
var item = new Item.ItemBuilder("medkit_01")
    .WithName("First Aid Kit")
    .WithCategory(ItemCategory.Medical)
    .WithHealth(100f)
    .WithMaxStackSize(5)
    .Build();

2. 配置类+精简构造函数

如果觉得构建器模式代码量偏大,可以用单独的配置类封装所有可选属性,构造函数仅接收必填项和可选的配置类参数。

实现示例

public enum ItemCategory
{
    // ... 枚举内容同上
}

// 配置类封装所有可选属性
public class ItemOptions
{
    public string? NameIdentifier { get; set; }
    public string? FallbackNameIdentifier { get; set; }
    public string? DescriptionIdentifier { get; set; }
    public string? Name { get; set; }
    // ... 省略其余重复属性
    public float? Scale { get; set; }
    public List<XmlElement>? Children { get; set; }
}

public class Item
{
    public required string Identifier { get; init; }
    public string? NameIdentifier { get; init; }
    // ... 所有属性定义同上

    // 精简构造函数:必填项 + 可选配置类
    public Item(string identifier, ItemOptions? options = null)
    {
        Identifier = identifier;
        if (options == null) return;

        NameIdentifier = options.NameIdentifier;
        FallbackNameIdentifier = options.FallbackNameIdentifier;
        // ... 为所有可选属性赋值
        Scale = options.Scale;
        Children = options.Children;
    }

    // GetAsXml方法同上
}

使用方式

var options = new ItemOptions
{
    Name = "First Aid Kit",
    Category = ItemCategory.Medical,
    Health = 100f,
    MaxStackSize = 5
};

var item = new Item("medkit_01", options);

3. 额外优化建议

  • 避免使用字典方案:字典虽然灵活,但会丢失类型安全,编译时无法检查属性名称和类型,后期维护极易出错,不推荐。
  • 优化XML序列化:当前GetAsXml用反射遍历属性,效率一般。如果XML结构固定,可改用XmlSerializer序列化,或手动映射属性,避免反射的性能开销。
  • 属性封装规范:用自动属性替代公开字段,用init关键字保证实例不可变(如需修改可改用set),提升代码安全性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 00:02:04