0

I want an action to be executed conditionally, so I decide to reassign it to an empty function like this:

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

I use myBool for my example lambda not to be empty, just placeholder not meaningfull logic. The problem is that without the intermediate variable myAction I cannot make the assignment directly in the true statement with the ternary operator like this:

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

Because of the ; of the () => { myBool = false; }; lambda I obtain that a : is expected. Makes sense, but then no matter what I tried I am not able to fit in the lambda directly.

I tried wrapping the lambda with (() => { myBool = false; };) or {() => { myBool = false; };} with no success.

Many time the compilation error I obtain is type of conditional expression cannot be determined because there is no implicit conversion between 'lambda expression' and 'lambda expression'

It surprises me also that in the false statement, the lambda fits.

Can a lambda be assigned in the true statement of the ternary operator? If so, how? If not, why then it is in the false statement?

rustyBucketBay
  • 4,320
  • 3
  • 17
  • 47

1 Answers1

1

The ternary operator is often unable to determine left/right side result types.

Specifically, you won't be able to write:

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

because ()=>{} has no specific type in C#. This expression can be automatically converted to, for example, System.Action, but the compiler won't guess it has to do it, if there's no hint anywhere. And with this exact form as above, there's no hint.

In such cases, try to be more specific and leave as little to be inferred as possible, and often that'll fix it.

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

or this may also work:

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

or even

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

since it has a specific type in at least one place, so other sides may be inferred. I'm not sure though, I didn't check it with a real compiler. First version with two new-Action will work for sure.

In your case, there also was a problem with that extra ; before :.

After de-oneline'ing it and formatting in a bit odd way, it's:

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

and here you see that each of those lambdas is a clean ()=>{ } with no following ;, and that ?: has a format of condition ? whentrue : whenfalse and that the ; only is at the end of whole var foo = value; expression.

--

EDIT: since you asked about casting like

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

please note that it looks more or less the same, just as unreadable as any other lambda+?:, and saves you exactly 2 characters as compared to:

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

With casting, you still have to provide a full type name of the Action, and use parentheses around the lambda, so the difference is only between new and (), so 2 characters. I usually don't use casting unless absolutely necessary, so I'd go with new here.

Also, since ?: + lambdas quickly gets illegible, and since the ()=>{} fallback is actually a perfectly reuseable no-op, there's not much reason for creating tons of such delegate objects, so my code would actually probalby look like this:

// 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
};

or, if for some reason, it can't be reused, then:

Action nah = () => {
    logic logic
    logic logic
    logic logic
};

Action yea = () => {
    logic logic
    myBool = false;
    logic logic
};

var myAction = doIt ? yea : nah;

I know the last one directly breaks the original idea you had about placing it in one line, but since I already discuss different formatting and code layout, then, really. More than one lambda in a single line is very bad for readability, and unless those lambdas are trivial, your coworkers or future readeres of that code will probably be grateful for this expanded layout :)

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • 2
    Thanks for your answer. In that case, wouldn't it be preferable a cast to tell the compiler instead of the `new Action` ?? `Action myAction = doIt ? (Action)(() => { myBool = false; }) : () => { };` – rustyBucketBay Apr 23 '21 at 14:34
  • @rustyBucketBay: it got longer than I thought, so I pasted it at the end of the answer – quetzalcoatl Apr 24 '21 at 05:51