如何将List<派生类>转为List<基类>且不破坏引用?
解决方案
方案1:改用协变接口类型直接赋值
List<T>本身不支持协变,但IList<T>、ICollection<T>这类接口支持引用类型的协变。把currentItemBasesList的定义改成IList<ItemBase>,就能直接把子类列表赋值给它,此时两个变量指向同一个底层列表,所有增删改操作都会同步:
// 修改字段定义 public IList<ItemBase> currentItemBasesList; // 赋值时直接引用原列表 currentItemBasesList = chestplateBasesList;
注意:这种方式下,不能通过currentItemBasesList添加非ArmorBase类型的ItemBase子类(比如WeaponBase),否则会抛出类型转换异常——因为底层列表本质是List<ArmorBase>,只能容纳ArmorBase实例。
方案2:编写列表包装类代理操作
如果必须保持currentItemBasesList为List<ItemBase>类型,或者需要更灵活的类型校验,可以写一个包装类,把对ItemBase列表的操作转发到底层的子类列表上。示例代码如下:
public class CovariantListWrapper<TBase, TDerived> : List<TBase> where TDerived : TBase { private readonly List<TDerived> _sourceList; public CovariantListWrapper(List<TDerived> sourceList) { _sourceList = sourceList; } // 重写核心操作方法,转发到源列表 public new void Add(TBase item) { if (item is TDerived derivedItem) _sourceList.Add(derivedItem); else throw new ArgumentException($"只能添加{typeof(TDerived).Name}类型的项"); } public new bool Remove(TBase item) { return item is TDerived derivedItem && _sourceList.Remove(derivedItem); } public new int Count => _sourceList.Count; public new TBase this[int index] { get => _sourceList[index]; set { if (value is TDerived derivedItem) _sourceList[index] = derivedItem; else throw new ArgumentException($"只能设置{typeof(TDerived).Name}类型的项"); } } // 根据需求补充实现Clear、Insert、RemoveAt等其他方法 }
使用时直接创建包装类实例赋值:
currentItemBasesList = new CovariantListWrapper<ItemBase, ArmorBase>(chestplateBasesList);
这样对currentItemBasesList的所有操作都会同步到chestplateBasesList,同时保留currentItemBasesList的List<ItemBase>类型。
方案3:标记当前选中列表,直接操作源列表
如果不需要currentItemBasesList作为统一操作入口,也可以用一个标记变量记录当前选中的列表类型,操作时直接对对应源列表执行操作:
// 定义枚举标记当前选中的列表类型 public enum SelectedItemCategory { Helmet, Chestplate, ShoulderGuards, Armguards, Thighguards, Boots, Shield, Cape, Weapon } public SelectedItemCategory currentSelectedCategory; // 示例:移除选中列表中的项 public void RemoveItemFromSelectedList(ItemBase item) { switch(currentSelectedCategory) { case SelectedItemCategory.Chestplate: chestplateBasesList.Remove(item as ArmorBase); break; case SelectedItemCategory.Weapon: weaponBasesList.Remove(item as WeaponBase); break; // 补充其他分支 } }
这种方式完全绕开了列表引用的问题,直接操作源列表,但需要额外逻辑来区分当前操作的目标列表。
内容的提问来源于stack exchange,提问作者Bruno Pasini Fiorini
相关产品推荐
相关产品推荐

