我该如何处理需要存储几种不相关类型但需要按需提供特定类型的情况?

时间:2020-03-05 18:50:46  来源:igfitidea点击:

我正在使用文件编辑器,供我们使用的重要内部测试工具使用。该工具本身很大,很复杂,在可预见的将来,重构或者重写所需要的资源将超出我们的能力,因此,在进行大的修改时,我会束手无策。我必须使用.NET语言。

这些文件是该工具使用的四个类的XML序列化版本(我们将其称为A,B,C和D)。当一切顺利的时候,这些类就形成了树形结构。我们的编辑器通过加载一组文件,反序列化它们,计算它们之间的关系以及跟踪它可以找到的任何不良状态来工作。这个想法是让我们摆脱手工编辑这些文件的麻烦,因为这会引入大量错误。

对于特定类型的错误,我想维护所有出现问题的文件的集合。所有四个类都可能有此问题,我想尽可能减少代码重复。一个重要的要求是用户需要能够成套获取物品。例如,他们需要获取所有带错误的A对象,并告诉它们遍历整个集合并选择与GetAs()方法相比不可接受的对象。因此,我的第一个想法是制作一个与反序列化对象和一些元数据相关的通用项目,以指示错误:

public class ErrorItem<T>
{
    public T Item { get; set; }
    public Metadata Metadata { get; set; }
}

然后,我将拥有一个可以容纳所有错误项的收集类,并带有帮助程序方法,以在用户需要它们时提取特定类的项。这是麻烦开始的地方。

没有一个类继承自一个共同的祖先(对象除外)。这可能是最初设计的一个错误,但是我花了几天的时间来思考它,并且这些类除了GUID属性可以唯一地标识每个项目外,实际上并没有太多共同之处,因此我可以看到为什么原始设计师没有通过继承联系他们。这意味着统一错误收集将需要存储ErrorItem &lt;Object>对象,因为我没有基类或者接口来限制传入的内容。但是,这使该统一收集的思想有点粗略我:

Public Class ErrorCollection
{
    public ErrorItem<Object> AllItems { get; set; }
}

但是,这会对公共接口产生影响。我真正想要的是返回适当的ErrorItem通用类型,如下所示:

public ErrorItem<A>[] GetA()

这是不可能的,因为我只能存储ErrorItem &lt;Object>!我已经解决了一些解决方法。大多数情况下,它们包括即时创建适当类型的新" ErrorItem",但感觉有点难看。另一个想法是使用"字典"将项目按类型组织起来,但似乎仍然不正确。

有某种模式可以帮助我吗?我知道解决此问题的最简单方法是添加一个派生自A,B,C和D的基类,但是我试图对原始工具的影响尽可能小。任何变通方法的成本是否足够大,我应该推动更改初始工具?

解决方案

回答

如果A,B,C和D没有共同点,那么添加基类将无法真正为我们带来任何好处。它只是一个空类,实际上与对象相同。

我只是创建一个没有泛型的ErrorItem类,将Item设置为对象,并在要使用引用的对象时进行一些强制转换。如果要使用Guid以外的A,B,C或者D类的任何属性或者方法,则无论如何都必须将其强制转换。

回答

这是你想要的?

private List<ErrorItem<object>> _allObjects = new List<ErrorItem<object>>();

public IEnumerable<ErrorItem<A>> ItemsOfA
{
    get
    {
        foreach (ErrorItem<object> obj in _allObjects)
        {
            if (obj.Item is A)
                yield return new ErrorItem<A>((A)obj.Item, obj.MetaData);
        }
    }
}

如果我们想缓存ItemsOfA,则可以轻松地做到这一点:

private List<ErrorItem<A>> _itemsOfA = null;

public IEnumerable<ErrorItem<A>> ItemsOfACached
{
    if (_itemsOfA == null)
        _itemsOfA = new List<ErrorItem<A>>(ItemsOfA);
    return _itemsOfA;
}

回答

到目前为止,我要回答的是fryguybob和Mendelt Siebenga的答案的组合。

正如Mendelt Siebenga指出的那样,添加基类只会污染名称空间并引入类似的问题。我可以更好地控制哪些项目可以放入集合中,但是我仍然需要存储ErrorItem &lt;BaseClass>并进行一些强制转换,因此在相同的根本原因下,我会有一个稍微不同的问题。这就是为什么我选择该帖子作为答案的原因:它指出无论如何我都必须进行一些强制转换,而KISS会指出额外的基类和泛型太多了。

我喜欢fryguybob的答案不是因为解决方案本身,而是因为它使我想起了"收益回报",这将使非缓存版本更易于编写(我将使用LINQ)。我认为缓存版本要更明智一些,尽管预期的性能参数不会使非缓存版本明显变慢。