C# 如何重写bool TrySomething(列出<;T>;项,列出<;T>;项失败)
我有这个功能:C# 如何重写bool TrySomething(列出<;T>;项,列出<;T>;项失败),c#,design-patterns,coding-style,out,C#,Design Patterns,Coding Style,Out,我有这个功能: bool TrySomething(IEnumerable<T> items, out List<T> listOfProblemItems) { //... return listOfProblemItems != null && listOfProblemItems.Count > 0; } 如果出现问题,返回true是不直观的,但是如果我将其更改为false,那么out参数将填充false的信息,从而打破“T
bool TrySomething(IEnumerable<T> items, out List<T> listOfProblemItems)
{
//...
return listOfProblemItems != null && listOfProblemItems.Count > 0;
}
如果出现问题,返回true
是不直观的,但是如果我将其更改为false
,那么out
参数将填充false
的信息,从而打破“TrySomething”模式
对我来说,抛出异常不是一种选择,因为这是一个高性能的项目,我需要一个明确定义的列表,列出哪些项目失败了
我可以让它返回问题列表并将其命名为trysomethin和returnanyproblems
,但这个名称很难看,调用代码必须检查返回的列表中的null和计数,我宁愿在函数中这样做,因为无论如何都必须这样做
List<T> problems = TrySomethingAndReturnAnyProblems(items);
if(problems != null && problems.Count > 0)
{
//ugly and introduces duplicate code every time the function is used.
}
List problems=trysomethin和returnanyproblems(项目);
if(problems!=null&&problems.Count>0)
{
//丑陋,每次使用该函数时都会引入重复代码。
}
如何在保持“TrySomething”模式的简洁性的同时,将其变成可读的、自文档化的函数定义?编写一个扩展方法来简单地过滤出有问题的项怎么样?这样,您只能取回正常的项目,例如:
public static IEnumerable<T> ExceptProblematic(this IEnumerable<T> items)
{
foreach (var item in items)
{
if (!IsProblematic(item))
yield return item;
}
}
或者,您可以编写一个查询以仅返回问题项,然后进行另一个查询以筛选出它们,例如:
var problemItems = items.Where(item => IsProblematic(item));
var validItems = items.Except(problemItems);
然后相应地处理问题项:
if (problemItems.Any())
{
// handle problem items
}
用
bool Success
和T Data
属性创建一个Result
类怎么样
class Result<T>
{
public bool Success {get; set;}
public T Data {get; set;}
}
如果您担心破坏
TryX
模式,为什么不编一个:
public bool AntiTrySomething(IEnumerable<T> input, out IEnumerable<T> fails)
{
// implement me - return true if fail
}
public bool AntiTrySomething(IEnumerable输入,out IEnumerable失败)
{
//实现me-如果失败则返回true
}
只要你在整个代码库中保持一致,我认为这样的东西是清楚的,它将你从“标准”中分离出来,在保持熟悉的同时不与之冲突。我宁愿为结果创建一个特殊的类(参见Ahmed KRAIEM的回答) 但我会用一些不同的(精心设计的)方式:
公共密封类SomeResultWithDescription:IReadOnlyList{
私有列表m_问题;
私有布尔m_Success=true;//0)
返回false;
返回true;
}
}
公共布尔ToBoolean(){
回归成功;
}
公共静态隐式运算符布尔值(SomeResultWithDescription值){
if(Object.ReferenceEquals(null,value))
返回false;
返回值。ToBoolean();
}
公共滴度此[整数索引]{
得到{
返回m_问题[索引];
}
}
公共整数计数{
得到{
返回m_.Count;
}
}
公共IEnumerator GetEnumerator(){
返回m_问题。GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){
返回m_问题。GetEnumerator();
}
}
...
//你的方法是
SomeResultWithDescription尝试测量(IEnumerable items)
{
//...
返回新的SomeResultWithDescription(问题项列表);
}
//所以你可以
// 1. 如果没有检测到问题,请执行此操作
如果(尝试(项目)){
...
}
// 2. 如果没有问题就做,如果有问题就分析
var结果=试验测量(项目);
如果(结果){
…//没问题
}
其他的{//code>return listOfProblemItems==null | | listOfProblemItems.Count==0;
?顺便说一句,我会删除bool
返回值并直接返回列表。调用方只需检查空列表……也许您不应该使用TryX
模式。TryX
方法使用out
参数作为结果,not用于报告问题。您正在不时地打破这种模式。首先,自定义异常类型当然可以包含一系列问题。其次,如果没有抛出异常,则异常不会很慢。换句话说,当您的程序无法工作时,它真的必须不能很快工作吗?最后但并非最不重要的是,异常可能不会很快发生就像你想象的那样慢。你正在建立一个问题列表,然后在这个列表上循环。这个过程中的异常成本很可能是可以忽略的。@KrisVandermotten:他正在编写一个经常失败的方法,否则这个方法本身将是多余的。int TrySomething怎么样(IEnumerable items,Action problemCallback)
?我不确定除了尝试运行操作并查看之外,IsProblematic(item)
会做什么。在这种情况下,我可以创建一个bool TrySomething(T item)
,我不能这么做,因为它是一个高性能函数,只有在传递大量项时才有用。
if (problemItems.Any())
{
// handle problem items
}
class Result<T>
{
public bool Success {get; set;}
public T Data {get; set;}
}
Result<List<T>> result = TrySomething(enumerable);
if (!result.Success)
{
List<T> problematicItems = result.Data;
//...
}
public bool AntiTrySomething(IEnumerable<T> input, out IEnumerable<T> fails)
{
// implement me - return true if fail
}
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) {
...
}
}