0

Declaring classes

class A
{
    public string a;
    public static implicit operator A(B b) => new A() { a = b.b };
}
class B
{
    public string b;
}

comparison ?: and if-else

static public A Method1(B b)
{ 
    return (b is null) ? null : b; //equally return b;
}
static public A Method2(B b)
{
    if (b is null) return null; else return b;
}

Method1 will throw an exception
Method2 will work fine

Method1(null);
Method2(null);

Why do they behave differently?

0xFF
  • 49
  • 3
  • Q: are "if/else" and the ternary operator "equivalent"? Sure, for the most part. Q: Was Anders Hejlsberg original decision to exclude operator overloading from C# a wise choice? Yes, definitely :) – paulsm4 Dec 15 '20 at 22:46
  • 1
    What about these two snippets don't you understand? Why do you think the first snippet shouldn't call the implicit operator when the value is null, or why do you think the second should? – Servy Dec 15 '20 at 22:50
  • There's a lot going on here, but... You don't need the IL to know that an `if` statement is a statement and the ternary operator is an expression. So no, not equivalent. The primary reason `?:` exists is to do ["inline if's;"](https://en.wikipedia.org/wiki/%3F:) they are a convenient shorthand that can be used in things like print statements. – Robert Harvey Dec 15 '20 at 22:54
  • @Богдан Technically it *is* possible to turn any arbitrary `if/else` into a use of a conditional operator (or the reverse). It's not always a good idea, but it's technically *possible*. Obviously in this case you did not do the conversion in a way that maintained the same semantics. You could have though. – Servy Dec 15 '20 at 22:57
  • 1
    Neither processors nor MSIL know anything about the conditional operator. So it needs to be translated to code that they *do* understand. Which is the same logic the if-statement uses. Translating from a rich syntax to crippled, but fast, executable code is the basic job of a language compiler. – Hans Passant Dec 15 '20 at 23:15
  • I have no idea how this was closed as opinion-based. There's absolutely nothing that is remotely subjective about it! I've edited the question to clarify it more and voted to reopen, @14832315. Feel free to edit it further if you think I messed something up. – 41686d6564 stands w. Palestine Dec 16 '20 at 07:08

2 Answers2

4

When using a ternary operator in Method1, the type of the return value must be the same in both branches (B, in your case). That value has to be cast to A later to match the return type of the method. So, a NullReferenceException is thrown in your implicit operator (because b is null).

In Method2, on the other hand, you're returning null directly; so, no cast is needed.

You get the same result (i.e., exception) in Method2 if you change it into something like this:

static public A Method2(B b)
{
    B temp;
    if (b is null) temp = null; else temp = b;
    return temp; // Throws a NullReferenceException.
}

What you ought to be doing is adding a null-check in the implicit operator:

public static implicit operator A(B b) => (b is null ? null : new A { a = b.b });
  • @Богдан Because the ternary operator must have a _unified_ return type. If one branch is retunring `B`, the other one must return `B` as well. Consider this `var test = someBool ? null : new Person();` and then ask yourself "what's the type of `test`?". – 41686d6564 stands w. Palestine Dec 15 '20 at 23:19
  • @Богдан There's no "or"; it must have a specific type. Let me quote from [the documentation](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/conditional-operator): _"If a target type of a conditional expression is unknown (for example, when you use the var keyword) or in C# 8.0 and earlier, the type of consequent and alternative must be the same or there must be an implicit conversion from one type to the other"_ – 41686d6564 stands w. Palestine Dec 15 '20 at 23:29
0

Method1

For the ternary operator type of return values should be equal. So null is actually returned as (B)null, which then uses implicit operator to get converted to type A

Method2

The return value is directly converted to (A)null, and implicit operator is not used.

Bonus

Try changing return type of Method1 to B and it should work.

    public static B Method1(B b)
    {
        return (b is null) ? null : b;                  
    }
Marshal
  • 6,551
  • 13
  • 55
  • 91