一个语言(如C#)如何实现一个新的逻辑运算符来处理这种情况?应该是?在C#中是否存在处理此案例的现有方法?其他语言?
例如,当我们假设我们正在寻找直接比较时,已经克服了一些降低三元或零合并运算符的冗长度的情况.请参阅Unique ways to use the Null Coalescing operator,特别是关于如何扩展运算符的使用以支持String.IsNullOrEmpty(string)的讨论.注意Jon Skeet如何使用MiscUtil
中的PartialComparer将0重新格式化为空值,
为什么这可能是必要的?那么,看看我们如何为没有任何快捷方式的复杂对象编写比较方法(引用的讨论中的示例):
public static int Compare( Person p1, Person p2 ) { return ( (result = Compare( p1.Age, p2.Age )) != 0 ) ? result : ( (result = Compare( p1.Name, p2.Name )) != 0 ) ? result : Compare( p1.Salary, p2.Salary ); }
Jon Skeet写了一个新的比较来回避平等案例.这允许通过编写一个返回null的新特定方法来扩展表达式,允许我们使用null合并运算符:
return PartialComparer.Compare(p1.Age, p2.Age) ?? PartialComparer.Compare(p1.Name, p2.Name) ?? PartialComparer.Compare(p1.Salary, p2.Salary) ?? 0;
空合并运算符更具可读性,因为它有两个边,而不是三个边. boolean condition子句被分隔为一个方法,在这种情况下,如果必须继续表达式,则返回null.
如果我们可以更容易地将条件置于线上,那么上面的表达式会是什么样的?从PartialComparer.Compare中获取返回null的表达式,并将其放在一个新的三元表达式中,该表达式允许我们使用左侧表达式的求值,并使用隐式临时变量值:
return Compare( p1.Age, p2.Age ) unless value == 0 : Compare( p1.Name, p2.Name ) unless value == 0 : Compare( p1.Salary, p2.Salary );
表达式的基本“流程”将是:
expression A unless boolean B in which case expression C
我认为这更像是一个短路的倒三元运算符,而不是一个重载的比较运算符.
>这种逻辑会有用吗?目前,null合并为我们提供了一种使用条件表达式(value == null)执行此操作的方法.
>您想要测试哪些其他表达方式?我们听说过(String.IsNullOrEmpty(value)).
>在操作符,关键字方面,用语言表达这一点的最佳方式是什么?
public static int CompareChain<T>(this int previous, T a, T b) { if (previous != 0) return previous; return Comparer<T>.Default.Compare(a,b); }
像这样使用:
int a = 0, b = 2; string x = "foo", y = "bar"; return a.Compare(b).CompareChain(x,y);
可以通过JIT内联,因此它可以像语言中内置的短路一样执行,而不会产生更多的复杂性.
在回答您询问上述“结构”是否可以应用于不仅仅是比较时,可以通过选择是否继续或不可由用户进行控制来实现.这本质上更复杂,但操作更灵活,因此这是不可避免的.
public static T ElseIf<T>( this T previous, Func<T,bool> isOK Func<T> candidate) { if (previous != null && isOK(previous)) return previous; return candidate(); }
然后像这样使用
Connection bestConnection = server1.GetConnection() .ElseIf(IsOk, server2.GetConnection) .ElseIf(IsOk, server3.GetConnection) .ElseIf(IsOk, () => null);
这是最大的灵活性,因为您可以在任何阶段更改IsOk检查并且完全是懒惰的.对于OK检查在每种情况下都是相同的情况,您可以像这样简化并完全避免扩展方法.
public static T ElseIf<T>( Func<T,bool> isOK IEnumerable<Func<T>[] candidates) { foreach (var candidate in candidates) { var t = candidate(); if (isOK(t)) return t; } throw new ArgumentException("none were acceptable"); }
您可以使用linq执行此操作,但这样可以提供一个很好的错误消息并允许此操作
public static T ElseIf<T>( Func<T,bool> isOK params Func<T>[] candidates) { return ElseIf<T>(isOK, (IEnumerable<Func<T>>)candidates); }
样式导致可读的代码如下:
var bestConnection = ElseIf(IsOk, server1.GetConnection, server2.GetConnection, server3.GetConnection);
如果要允许默认值,则:
public static T ElseIfOrDefault<T>( Func<T,bool> isOK IEnumerable<Func<T>>[] candidates) { foreach (var candidate in candidates) { var t = candidate(); if (isOK(t)) return t; } return default(T); }
显然,以上所有内容都可以使用lambdas编写,因此您的具体示例如下:
var bestConnection = ElseIfOrDefault( c => c != null && !(c.IsBusy || c.IsFull), server1.GetConnection, server2.GetConnection, server3.GetConnection);