当前位置 : 主页 > 编程语言 > c语言 >

操作方法:在例如实现的短路反向三元运算符中实现. C#?有关系吗?

来源:互联网 收集:自由互联 发布时间:2021-06-25
假设您正在使用三元运算符或空合并运算符或嵌套的if-else语句来选择对象的赋值.现在假设在条件语句中,您对昂贵或易失性操作进行了评估,要求将结果放入临时变量中,捕获其状态,以便
假设您正在使用三元运算符或空合并运算符或嵌套的if-else语句来选择对象的赋值.现在假设在条件语句中,您对昂贵或易失性操作进行了评估,要求将结果放入临时变量中,捕获其状态,以便可以对其进行比较,然后进行可能的分配.

一个语言(如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);
网友评论