如何:在中实现短路反向三值运算符,例如C#?这有关系吗?
假设您正在使用三元运算符、空合并运算符或嵌套的if-else语句来选择对对象的赋值。现在假设在条件语句中,您对一个昂贵的或易失的操作进行了求值,要求您将结果放入一个临时变量中,捕获其状态,以便对其进行比较,然后可能进行赋值 一种语言,例如C#,如何实现一个新的逻辑运算符来处理这种情况?应该吗?在C#中是否存在处理此情况的现有方法?其他语言 例如,当我们假设我们正在寻找直接比较时,减少三元或空合并运算符的冗长性的一些情况已经被克服。请特别参阅关于如何扩展运算符的使用以支持如何:在中实现短路反向三值运算符,例如C#?这有关系吗?,c#,language-design,language-features,C#,Language Design,Language Features,假设您正在使用三元运算符、空合并运算符或嵌套的if-else语句来选择对对象的赋值。现在假设在条件语句中,您对一个昂贵的或易失的操作进行了求值,要求您将结果放入一个临时变量中,捕获其状态,以便对其进行比较,然后可能进行赋值 一种语言,例如C#,如何实现一个新的逻辑运算符来处理这种情况?应该吗?在C#中是否存在处理此情况的现有方法?其他语言 例如,当我们假设我们正在寻找直接比较时,减少三元或空合并运算符的冗长性的一些情况已经被克服。请特别参阅关于如何扩展运算符的使用以支持String.IsNull
String.IsNullOrEmpty(String)
的讨论。注意如何使用PartialComparer
from将0
s重新格式化为null
s
为什么这可能是必要的?那么,看看我们是如何为没有任何捷径的复杂对象编写比较方法的(引用讨论中的示例):
Jon Skeet写了一个新的比较来回退平等案例。这允许表达式通过编写新的特定方法进行扩展,该方法返回null,从而允许我们使用null合并运算符:
return PartialComparer.Compare(p1.Age, p2.Age)
?? PartialComparer.Compare(p1.Name, p2.Name)
?? PartialComparer.Compare(p1.Salary, p2.Salary)
?? 0;
空合并运算符更具可读性,因为它有两条边,而不是三条边。布尔条件子句被分割成一个方法,在这种情况下,如果表达式必须继续,则返回null
如果我们能更容易地将条件串联起来,上面的表达式会是什么样子?从PartialComparer获取表达式。比较返回null
的
,并将其放入一个新的三元表达式中,该表达式允许我们使用一个隐式临时变量值来计算左侧表达式:
return Compare( p1.Age, p2.Age ) unless value == 0
: Compare( p1.Name, p2.Name ) unless value == 0
: Compare( p1.Salary, p2.Salary );
表达式的基本“流”是:
var bestConnection = ElseIfOrDefault(
c => c != null && !(c.IsBusy || c.IsFull),
server1.GetConnection,
server2.GetConnection,
server3.GetConnection);
表达式A除非布尔B在这种情况下表达式C
与其说它是一个重载比较运算符,不如说它更像是一个短路反向三值运算符
- 这种逻辑有用吗?目前,空合并为我们提供了一种通过条件表达式
(value==null)
实现这一点的方法
- 您还想测试哪些其他表达式?我们听说过
(String.IsNullOrEmpty(value))
- 在语言中,用运算符、关键字来表达这一点的最佳方式是什么
要使一个建议的实现远离一个非常冗长的问题,让我们使用除非关键字运行
(表达式A)除非(布尔B)(表达式C)
。。。这就是一切
布尔表达式B可以通过关键字value
访问表达式A的计算。表达式C可以在其表达式中包含,除非
关键字,允许简单的线性链接
选举候选人:
:
|
?:
否则
关键字
任何符号的使用都会降低普通开发人员的可读性。即使是?
操作符也没有广泛使用。一、 我自己确实更喜欢开发冗长的代码,但一年后我就可以轻松阅读了
因此,您的候选人:
表达式A,除非布尔值B在这种情况下为表达式C
会是
表达式A除非是布尔B,否则是表达式C
尽管许多像我这样的人仍然会使用:
if (B) {expression C;}
else {expression A;}
当你与一个拥有不同背景的大团队一起开发软件时,就会出现这种情况,团队中的每一个人都掌握一种语言,而且只是其他人的用户。我个人认为,我会避免操作人员的短路,而只是让方法将其链接起来:
public static int CompareChain<T>(this int previous, T a, T b)
{
if (previous != 0)
return previous;
return Comparer<T>.Default.Compare(a,b);
}
可以由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();
}
这是最大的灵活性,因为您可以在任何阶段更改IsOk检查,并且完全是懒惰的。对于“是否正常”检查在任何情况下都是相同的情况,您可以这样简化,完全避免扩展方法
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");
}
如果要允许默认值,则:
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);
}
More:啊,我看这可能不仅仅适用于比较??我没有使用C#3和扩展方法,但我想您可以在下面的示例中声明一个
public delegate bool Validation<T>( T toTest );
public static T Validate<T>( this T leftside, Validation<T> validator )
{
return validator(leftside) ? leftside : null;
}
好的,我们可以有一个方法
bool IsOk( Connection c )
{
return ( c != null && !(c.IsBusy || c.IsFull) );
}
这将产生:
Connection temp;
Connection bestConnection =
( temp = server1.GetConnection() ) && IsOk( temp ) ? temp
: ( temp = server2.GetConnection() ) && IsOk( temp ) ? temp
: ( temp = server3.GetConnection() ) && IsOk( temp ) ? temp
: null;
但在这里,用于比较的方法链接是如何工作的呢?我在思考一件事情,它看起来像:
Connection bestConnection =
server1.GetConnection() unless !IsOk(value) otherwise
server2.GetConnection() unless !IsOk(value) otherwise
server3.GetConnection() unless !IsOk(value) otherwise null;
我认为,到目前为止,如果我希望条件的结果是原始条件中的一个表达式或方法的结果,那么有很多困难需要解决
我假设这些方法返回的对象的生成成本很高,或者在下次调用该方法时会发生更改。对于这个问题,您已经有了很多很好的答案,而我在这个特定的聚会上迟到了。然而,我认为值得注意的是,你的建议是一种更普遍有用的操作的特例,我非常希望C#拥有这种操作,即在表达式上下文中为临时计算命名的能力<
var bestConnection = ElseIfOrDefault(
c => c != null && !(c.IsBusy || c.IsFull),
server1.GetConnection,
server2.GetConnection,
server3.GetConnection);
public delegate bool Validation<T>( T toTest );
public static T Validate<T>( this T leftside, Validation<T> validator )
{
return validator(leftside) ? leftside : null;
}
Validation<Connection> v = ( Connection c ) => ( c != null && !( c.IsBusy || c. IsFull ) );
Connection bestConnection =
server1.GetConnection().Validate( v ) ??
server2.GetConnection().Validate( v ) ??
server3.GetConnection().Validate( v ) ?? null;
Connection temp;
Connection bestConnection =
( temp = server1.GetConnection() ) != null && !(temp.IsBusy || temp.IsFull) ? temp
: ( temp = server2.GetConnection() ) != null && !(temp.IsBusy || temp.IsFull ) ? temp
: ( temp = server3.GetConnection() ) != null && !(temp.IsBusy || temp.IsFull ) ? temp
: null;
bool IsOk( Connection c )
{
return ( c != null && !(c.IsBusy || c.IsFull) );
}
Connection temp;
Connection bestConnection =
( temp = server1.GetConnection() ) && IsOk( temp ) ? temp
: ( temp = server2.GetConnection() ) && IsOk( temp ) ? temp
: ( temp = server3.GetConnection() ) && IsOk( temp ) ? temp
: null;
Connection bestConnection =
server1.GetConnection() unless !IsOk(value) otherwise
server2.GetConnection() unless !IsOk(value) otherwise
server3.GetConnection() unless !IsOk(value) otherwise null;
public static int Compare(Person p1, Person p2) =>
let ages = Compare(p1.Age, p2.Age) in
ages != 0 ?
ages :
let names = Compare(p1.Name, p2.Name) in
names != 0 ?
names :
Compare(p1.Salary, p2.Salary);
A() unless B() : C()
let a = A() in B() ? C() : a