我有这个功能:
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”模式的简洁性的同时,将其变为可读的自我记录功能定义?
答案 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
}
只要你在整个代码库中保持一致,我认为这样的事情是清楚的,并将你与“标准”区分开来,不要与它发生冲突,同时保持熟悉。