如何重写bool TrySomething(List <t> items,out List <t> itemsThatFailed)</t> </t>

时间:2013-11-20 12:44:33

标签: c# design-patterns coding-style out

我有这个功能:

bool TrySomething(IEnumerable<T> items, out List<T> listOfProblemItems)
{
    //...

    return listOfProblemItems != null && listOfProblemItems.Count > 0;
}

现在与“TrySomething”模式所期望的相反(例如TryParse):

如果出现问题,则返回true(表示out参数已填充信息)。所以我可以这样称呼它:

if(TrySomething(items, out problemItems))
{
   //handle problems
   //"but.. it returned 'true'! Why are there problems? I'm confused."
}

如果遇到问题,返回true并不直观,但如果我将其更改为false,那么out参数将填充false上的信息,从而打破了“TrySomething”模式。

if(TrySomething(items, out problemItems) == false)
{
   //handle problems
   //"the 'out' parameter is used? but it returned false! I'm confused."
}

不能替代我抛出异常,因为这是一个高性能的作品,我需要一个明确定义的列表,无论如何哪些项目都失败了。

我可以让返回问题列表并将其命名为TrySomethingAndReturnAnyProblems,但该名称很难看,调用代码必须检查返回的列表是否为null和计数,我宁愿完成这个功能,因为无论如何它总是必须完成。

List<T> problems = TrySomethingAndReturnAnyProblems(items);
if(problems != null && problems.Count > 0)
{
    //ugly and introduces duplicate code every time the function is used.
}

如何在保留“TrySomething”模式的简洁性的同时,将其变为可读的自我记录功能定义?

4 个答案:

答案 0 :(得分:2)

如何编写扩展方法来简单地过滤掉有问题的项目?这样,你只能找回好的项目,例如:

public static IEnumerable<T> ExceptProblematic(this IEnumerable<T> items)
{
  foreach (var item in items)
  {
    if (!IsProblematic(item))
      yield return item;
  }
}

这样你就可以使用它:

var validItems = items.ExceptProblematic();

或者,您可以编写查询以仅返回问题项,然后再进行另一个查询以将其过滤掉,例如:

var problemItems = items.Where(item => IsProblematic(item));
var validItems = items.Except(problemItems);

然后相应地处理问题项目:

if (problemItems.Any()) 
{
   // handle problem items
}

答案 1 :(得分:2)

如何使用Result<T>bool Success属性创建T Data课程?

class Result<T>
{
     public bool Success {get; set;}
     public T Data {get; set;}
}

然后让TrySomething返回Result<List<T>>

Result<List<T>> result = TrySomething(enumerable);
if (!result.Success)
{
    List<T> problematicItems = result.Data;
    //...
}

答案 2 :(得分:1)

我宁愿为结果创建一个特殊课程(请参阅Ahmed KRAIEM的答案) 但我会用一些不同的(精心设计的)方式来做:

  public sealed class SomeResultWithDescription<TItem>: IReadOnlyList<TItem> {
    private List<TItem> m_Problems;
    private Boolean m_Success = true; // <- the field may be redundant 

    // may be redundant: if there's no situation when the result is falure
    // even if there're no problems enlisted
    internal SomeResultWithDescription(List<TItem> problems, Boolean success)
      : this(problems) {

      m_Success = success;
    }

    internal SomeResultWithDescription(List<TItem> problems)
      : base() {

      if (Object.ReferenceEquals(null, problems))
        m_Problems = new List<TItem>();
      else
        m_Problems = problems;
    }

    public IReadOnlyList<TItem> Problems {
      get {
        return m_Problems;
      }
    }

    public Boolean Success {
      get {
        if (!m_Success)
          return false;
        else if (m_Problems.Count > 0)
          return false;

        return true;
      }
    }

    public Boolean ToBoolean() {
      return Success;
    }

    public static implicit operator Boolean(SomeResultWithDescription<TItem> value) {
      if (Object.ReferenceEquals(null, value))
        return false;

      return value.ToBoolean();
    }

    public TItem this[int index] {
      get {
        return m_Problems[index];
      }
    }

    public int Count {
      get {
        return m_Problems.Count;
      }
    }

    public IEnumerator<TItem> GetEnumerator() {
      return m_Problems.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
      return m_Problems.GetEnumerator();
    }
  }

  ...

  // Your method will be
  SomeResultWithDescription<T> TrySomething(IEnumerable<T> items)
  {
    //...

    return new SomeResultWithDescription<T>(listOfProblemItems);
  }

  // So you can do
  // 1. Just do if no problems detected
  if (TrySomething(items)) { 
    ...
  }

  // 2. Do if no problems, analyze if there're problems
  var result = TrySomething(items);

  if (result) { 
    ... // no problems
  }
  else { // <- some problems to analyze
    foreach (var problem in result) {
      ...
    }
  }

答案 3 :(得分:0)

如果您担心违反TryX模式,为什么不提出这个模式:

public bool AntiTrySomething(IEnumerable<T> input, out IEnumerable<T> fails)
{
    // implement me - return true if fail
}

只要你在整个代码库中保持一致,我认为这样的事情是清楚的,并将你与“标准”区分开来,不要与它发生冲突,同时保持熟悉。