如何:在中实现短路反向三值运算符,例如C#?这有关系吗?

如何:在中实现短路反向三值运算符,例如C#?这有关系吗?,c#,language-design,language-features,C#,Language Design,Language Features,假设您正在使用三元运算符、空合并运算符或嵌套的if-else语句来选择对对象的赋值。现在假设在条件语句中,您对一个昂贵的或易失的操作进行了求值,要求您将结果放入一个临时变量中,捕获其状态,以便对其进行比较,然后可能进行赋值 一种语言,例如C#,如何实现一个新的逻辑运算符来处理这种情况?应该吗?在C#中是否存在处理此情况的现有方法?其他语言 例如,当我们假设我们正在寻找直接比较时,减少三元或空合并运算符的冗长性的一些情况已经被克服。请特别参阅关于如何扩展运算符的使用以支持String.IsNull

假设您正在使用三元运算符、空合并运算符或嵌套的if-else语句来选择对对象的赋值。现在假设在条件语句中,您对一个昂贵的或易失的操作进行了求值,要求您将结果放入一个临时变量中,捕获其状态,以便对其进行比较,然后可能进行赋值

一种语言,例如C#,如何实现一个新的逻辑运算符来处理这种情况?应该吗?在C#中是否存在处理此情况的现有方法?其他语言

例如,当我们假设我们正在寻找直接比较时,减少三元或空合并运算符的冗长性的一些情况已经被克服。请特别参阅关于如何扩展运算符的使用以支持
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