1

假设您必须处理一个序列,InputType该序列产生两个序列,一个是 type OutputType,另一个是 type ErrorType

一个基本的实现可能是:

class SeqProcessor {
  private IEnumerable<ErrorType> errorTypes;

  public SeqProcessor()
  {
    this.errorTypes = Enumerable.Empty<ErrorType>;
  }

  public IEnumerable<ErrorType> Errors
  {
    get { return this.errors; } 
  }

  public IEnumerable<OutputType> ProcessItems(IEnumerable<InputType> inputTypes)
  {
     yield return new OutputType();
     if (err) this.errorTypes = this.errorTypes.Concat(new ErrorType());
     yield return new OutputType();
     yield return new OutputType();
     if (err) this.errorTypes = this.errorTypes.Concat(new ErrorType());
     // ...
     yield break;
  }
}

例如,我看到这两种选择:

  • 在and和 let return之间使用通用接口(例如IProduct)(而不是使用 Linq 进行区分)。OutputTypeErrorTypeProcessItemsIEnumerable<IProduct>

  • 定义一个ErrorType被调用的子类,NoError并让ProcessItems返回元组IEnumerable<Tuple<OutputType, ErrorType>>(如果没有错误,NoError将在元组中使用)。

编辑:

由于ErrorType在语义上不同OutputType,混合这些类型可能违反单一责任原则。委托的使用能否成为可接受的替代设计:

class SeqProcessor {
  public IEnumerable<OutputType> ProcessItems(
    IEnumerable<InputType> inputTypes,
    Action<ErrorType> onError)
  {
    yield return new OutputType();
    // ...
    onError(new ErrorType());
  }
}

在这种情况下,您使用哪种方法?

4

2 回答 2

2

第二种方法表明 NoError 实例是 NoError 的特化;这在实践中很少是真的。更有可能两者之间的共享功能很小,从而使第一种方法更好。

于 2013-03-13T13:07:31.083 回答
1

根据您想要实现的目标,我在这里看到了多种可能的解决方案:

  1. 保留原始实现(我将替换private IEnumerable<ErrorType> errorTypes它,但可以让您确定错误所属的项目)。在这种情况下,您遇到的错误将具有警告的意义(这也是我更喜欢 name 的Warning原因),因为它们与实际结果是分开的。

  2. 仅当使用结果列表的其他函数确实可以使用错误输出时,对两种结果类型(即输出和错误)使用通用接口才有意义。我怀疑这是您的意图,但恕我直言,这将是有效的设计选择。

  3. 正如 Pieter 指出的那样,拥有一个子类NoError真的ErrorType很讨厌。但是,更好的解决方案是使用和ResultType类型作为基础。这样,您就真正拥有了基类的专业化。不过,我想知道如果出现错误,输出是否会包含在内。原始元素?已处理但无效的元素?无效的?根据您想要实现的目标,这可能是合理的,但这很难从给定的信息中分辨出来,老实说,我怀疑这就是您想要的。NoErrorError

  4. OnError在许多情况下都是很好的做法,因为它具有很大的灵活性。但是,在这种情况下,您仍然需要考虑结果中的相应条目是什么。恕我直言,将它简单地排除在外可能是最好的选择,以避免处理任何一个null或任何一个特殊值。

总而言之,这种OnError方法似乎是最有希望的,尽管额外的信息可能会促使您采用其他提到的方法之一。

于 2013-03-18T13:19:34.300 回答