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

如何优雅实现Animal派生类的强类型字典集合并优化性能?

问题描述

我有三个类:

  • Animal
  • Giraffe : Animal
  • Tiger : Animal

想要实现三个字典类:

public abstract class AnimalDictionary
{
    internal Dictionary<int, Animal> _internalDictionary;

    //TODO: Dictionary interface implementation, minus the "Add" method
}

public class GiraffeDictionary : AnimalDictionary, IDictionary<int, Giraffe>
{
    //Collection Code Examples
    public List<Giraffe> GetGiraffesWithHerdID(int herdID);
    public void FleeFromTigers(TigerDictionary pride);
}

public class TigerDictionary : AnimalDictionary, IDictionary<int, Tiger>
{
    //Collection Code Examples
    public List<Tiger> GetActiveHunters();
    public void HuntGiraffeHerd(GiraffeDictionary targetHerd);
}

我清楚基类AnimalDictionary不能实现IDictionary或ICollection,否则会出现把Tiger放进GiraffeDictionary的类型安全问题——我虽然没完全搞懂协变与逆变,但明白不能让老虎进长颈鹿围栏的道理。

于是我让AnimalDictionary继承IEnumerable<KeyValuePair<int, Animal>>,实现除Add()外的所有字典方法,Add()交给派生类实现。

但在派生集合类实现IDictionary时,写出了不优雅的代码:

ICollection<Giraffe> IDictionary<int, Giraffe>.Values => _internalDictionary.Values.Cast<Giraffe>().ToList();

这让我很担心,因为需要在密集循环里遍历这些集合。我希望遍历Values集合时避免类型转换和创建新列表的性能开销,但Values是ICollection而非IEnumerable。

请问该怎么解决这个问题?有没有我忽略的替代方案?还是应该放弃基类,接受派生类里存在重复方法?


解决方案

1. 封装类型安全的ICollection实现,避免ToList()开销

你可以自行实现一个ICollection<T>包装类,直接对_internalDictionary.Values做类型转换包装,而非每次创建新列表。这样遍历仅在访问元素时做一次类型转换,不会额外生成集合副本。

示例代码:

internal class TypedCollectionWrapper<T> : ICollection<T> where T : Animal
{
    private readonly ICollection<Animal> _source;

    public TypedCollectionWrapper(ICollection<Animal> source)
    {
        _source = source;
    }

    public int Count => _source.Count;
    public bool IsReadOnly => _source.IsReadOnly;

    public void Add(T item) => throw new NotSupportedException("Values collection is read-only");
    public void Clear() => throw new NotSupportedException("Values collection is read-only");
    public bool Contains(T item) => _source.Contains(item);
    public void CopyTo(T[] array, int arrayIndex)
    {
        for (int i = 0; i < _source.Count; i++)
        {
            array[arrayIndex + i] = (T)_source.ElementAt(i);
        }
    }
    public IEnumerator<T> GetEnumerator() => _source.Cast<T>().GetEnumerator();
    public bool Remove(T item) => throw new NotSupportedException("Values collection is read-only");
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

派生类中这样实现Values属性:

ICollection<Giraffe> IDictionary<int, Giraffe>.Values => new TypedCollectionWrapper<Giraffe>(_internalDictionary.Values);

2. 改用泛型基类,从根源避免类型转换

放弃原AnimalDictionary抽象类,改用泛型基类AnimalDictionary<T>(T : Animal)。基类直接持有Dictionary<int, T>,派生类无需类型转换,同时保证类型安全。

示例代码:

public abstract class AnimalDictionary<T> : IDictionary<int, T> where T : Animal
{
    protected Dictionary<int, T> _internalDictionary = new Dictionary<int, T>();

    // 实现IDictionary<int, T>通用方法
    public bool ContainsKey(int key) => _internalDictionary.ContainsKey(key);
    public bool Remove(int key) => _internalDictionary.Remove(key);
    public bool TryGetValue(int key, out T value) => _internalDictionary.TryGetValue(key, out value);
    public T this[int key]
    {
        get => _internalDictionary[key];
        set => _internalDictionary[key] = value;
    }

    public ICollection<int> Keys => _internalDictionary.Keys;
    public ICollection<T> Values => _internalDictionary.Values;

    // Add方法留给派生类实现(如需控制添加逻辑)
    public abstract void Add(int key, T value);
}

public class GiraffeDictionary : AnimalDictionary<Giraffe>
{
    public override void Add(int key, Giraffe value)
    {
        // 长颈鹿特有验证逻辑
        _internalDictionary.Add(key, value);
    }

    public List<Giraffe> GetGiraffesWithHerdID(int herdID)
    {
        return _internalDictionary.Values.Where(g => g.HerdID == herdID).ToList();
    }

    public void FleeFromTigers(TigerDictionary pride)
    {
        // 业务逻辑实现
    }
}

public class TigerDictionary : AnimalDictionary<Tiger>
{
    public override void Add(int key, Tiger value)
    {
        // 老虎特有验证逻辑
        _internalDictionary.Add(key, value);
    }

    public List<Tiger> GetActiveHunters()
    {
        return _internalDictionary.Values.Where(t => t.IsActiveHunter).ToList();
    }

    public void HuntGiraffeHerd(GiraffeDictionary targetHerd)
    {
        // 业务逻辑实现
    }
}

该方案从根源消除类型转换需求,性能最优,同时保留代码复用性——基类实现通用字典方法,派生类仅处理特有逻辑。

3. 妥协方案(不推荐):显式实现IEnumerable

若不想修改基类结构,可在派生类将IDictionary<int, T>的Values属性显式实现为IEnumerable<T>,但这违反接口规范,仅适用于大部分场景仅需遍历Values的情况,可能导致调用方出错。


总结

优先推荐泛型基类方案,既保证类型安全,又消除性能开销,代码结构更清晰。若无法修改基类结构,则使用TypedCollectionWrapper包装类,既能满足接口要求,又避免不必要的集合复制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 18:05:37