【问题标题】:assign action with ternary operator c# [duplicate]使用三元运算符c#分配动作[重复]
【发布时间】:2021-07-17 18:13:44
【问题描述】:

我希望有条件地执行一个动作,所以我决定将它重新分配给一个空函数,如下所示:

public void ActionReassingnment1(bool doIt) {
    bool myBool = true;
    Action myAction = () => { myBool = false; };
    myAction = doIt ? myAction : () => { };
}

对于我的示例 lambda,我使用 myBool 不是空的,只是占位符没有意义的逻辑。 问题是,如果没有中间变量myAction,我无法使用三元运算符直接在真实语句中进行赋值,如下所示:

public void ActionReassingnment2(bool doIt)
{
    bool myBool = true;
    //Action myAction = () => { myBool = false; };
    myAction = doIt ? () => { myBool = false; }; : () => { }; //Error here
}

由于() => { myBool = false; }; lambda 中的;,我知道: 是预期的。有道理,但无论我尝试什么,我都无法直接适应 lambda。

我尝试用 (() => { myBool = false; };){() => { myBool = false; };} 包装 lambda,但没有成功。

很多时候我得到的编译错误是type of conditional expression cannot be determined because there is no implicit conversion between 'lambda expression' and 'lambda expression'

令我惊讶的是,在错误的陈述中,lambda 适合。

可以在三元运算符的真语句中赋值 lambda 吗?如果是这样,怎么做?如果不是,那为什么在虚假陈述中呢?

【问题讨论】:

    标签: c# lambda anonymous-function conditional-operator


    【解决方案1】:

    三元运算符通常无法确定左侧/右侧结果类型。

    具体来说,你不会写:

    xxx ? () => { } : () => { }
    

    因为()=>{} 在 C# 中没有特定类型。例如,这个表达式可以自动转换为System.Action,但如果没有任何提示,编译器不会猜测它必须这样做。有了上面这个确切的形式,就没有任何提示了。

    在这种情况下,请尝试更具体,尽可能少地进行推断,这通常会解决问题。

    var myAction = doIt ? new Action(() => { myBool = false; }) : new Action(() => { });
    

    或者这也可以工作:

    var myAction = doIt ? new Action(() => { myBool = false; }) : () => { }; // should work
    

    甚至

    Action myAction = doIt ? () => { myBool = false; } : () => { }; // really doubtful, probably not
    

    因为它至少在一个地方有一个特定的类型,所以可以推断出其他方面。不过我不确定,我没有用真正的编译器检查它。带有两个 new-Action 的第一个版本肯定可以工作。

    在您的情况下,;: 之前的那个额外的; 也存在问题。

    在对它进行脱线并以一种有点奇怪的方式格式化之后,它是:

    var myAction =
        doIt
        ? new Action(() => { myBool = false; })
        : new Action(() => { })
        ;
    

    在这里你看到每个 lambdas 都是一个干净的 ()=>{ } 没有后续 ;,并且 ?: 的格式为 condition ? whentrue : whenfalse 并且 ; 仅在整个末尾var foo = value; 表达式。

    --

    编辑:因为你问过像这样的演员

    var myAction = doIt ? (Action)(() => { myBool = false; }) : () => { };
    

    请注意,它看起来或多或少相同,就像任何其他 lambda+?: 一样不可读,并且与:

    var myAction = doIt ? new Action(() => { myBool = false; }) : () => { };
    

    使用强制转换,您仍然需要提供 Action 的完整类型名称,并在 lambda 周围使用括号,因此区别仅在 new () 之间,即 2 个字符。除非绝对必要,否则我通常不使用强制转换,所以我会在这里使用new

    另外,由于?: + lambdas 很快变得难以辨认,并且由于()=>{} 回退实际上是一个完全可重用的空操作,因此没有太多理由创建大量此类委托对象,所以我的代码实际上看起来很可能像这样:

    // elsewhere in some utility class
    public static readonly Action NoOp = () => { };
    
    ...
    
    var myAction = !doIt ? Utils.NoOp : () => { myBool = false; };
    
    // or, if code block is larger
    
    var myAction = !doIt ? Utils.NoOp : () => {
        logic logic
        myBool = false;
        logic logic
    };
    

    或者,如果由于某种原因,它不能被重复使用,那么:

    Action nah = () => {
        logic logic
        logic logic
        logic logic
    };
    
    Action yea = () => {
        logic logic
        myBool = false;
        logic logic
    };
    
    var myAction = doIt ? yea : nah;
    

    我知道最后一个直接打破了您将其放在一行中的原始想法,但是由于我已经讨论了不同的格式和代码布局,所以,真的。一行中的多个 lambda 对可读性非常不利,除非这些 lambda 是微不足道的,否则您的同事或该代码的未来读者可能会感谢这种扩展布局:)

    【讨论】:

    • 感谢您的回答。在那种情况下,告诉编译器而不是 new Action 不是更可取的演员吗? Action myAction = doIt ? (Action)(() => { myBool = false; }) : () => { };
    • @rustyBucketBay:它比我想象的要长,所以我把它贴在答案的末尾
    猜你喜欢
    • 2020-06-20
    • 2020-04-26
    • 2015-04-04
    • 1970-01-01
    • 2023-03-05
    • 2012-02-02
    • 2021-03-31
    • 2019-07-14
    • 2011-05-05
    相关资源
    最近更新 更多