1

我正在为我们使用的重要内部测试工具使用的文件开发一个编辑器。该工具本身又大又复杂,重构或重写所占用的资源比我们在可预见的未来所能投入的资源还要多,所以当涉及到大的修改时,我的双手束手无策。我必须使用 .NET 语言。

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

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

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

然后,我将有一个可以保存所有错误项的集合类,并使用辅助方法在用户需要时提取特定类的项。这就是麻烦开始的地方。

没有一个类继承自一个共同的祖先(除了Object)。这可能是最初设计的一个错误,但我花了几天时间思考它,除了唯一标识每个项目的 GUID 属性之外,这些类确实没有太多共同点,所以我可以明白为什么最初的设计师没有通过继承将它们联系起来。这意味着统一的错误集合需要存储ErrorItem<Object>对象,因为我没有基类或接口来限制进来的内容。但是,这让这个统一集合的想法对我来说有点粗略:

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

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

public ErrorItem<A>[] GetA()

这是不可能的,因为我只能存储ErrorItem<Object>!我已经在脑海中研究了一些解决方法;大多数情况下,它们包括在运行中创建一个ErrorItem合适类型的新的,但它只是感觉有点难看。另一个想法是使用 aDictionary来按类型组织项目,但它似乎仍然不正确。

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

4

3 回答 3

1

这是你想要的?

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;
}
于 2008-09-10T14:57:36.077 回答
1

到目前为止,我要使用的答案是来自fryguybob 和Mendelt Siebenga 的答案的组合。

正如 Mendelt Siebenga 所指出的,添加基类只会污染命名空间并引入类似的问题。我会更好地控制哪些项目可以进入收藏,但我仍然需要存储ErrorItem<BaseClass>并仍然进行一些铸造,所以我会遇到一个稍微不同的问题,原因是相同的。这就是我选择这篇文章作为答案的原因:它指出无论如何我都必须进行一些强制转换,而 KISS 会规定额外的基类和泛型太多了。

我喜欢fryguybob 的回答不是为了解决方案本身,而是为了提醒我关于yield return,这将使非缓存版本更容易编写(我打算使用LINQ)。我认为缓存版本更明智一些,尽管预期的性能参数不会使非缓存版本明显变慢。

于 2008-09-10T16:48:01.240 回答
0

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

我只是创建一个没有泛型的 ErrorItem 类,使 Item 成为一个对象,并在您想要使用引用的对象时进行一些转换。如果您想使用除 Guid 之外的 A、B、C 或 D 类的任何属性或方法,则无论如何都必须强制转换它们。

于 2008-09-10T14:47:35.383 回答