C#'s Null Coalescing Operator () in PHP

The null-coalescing operator and throw

According to https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-7.0/throw-expression

a throw expression consists of the throw keyword followed by a null_coalescing_expression where the null_coalescing_expression

must denote a value of the class type System.Exception, of a class
type that derives from System.Exception or of a type parameter type
that has System.Exception (or a subclass thereof) as its effective
base class. If evaluation of the expression produces null, a
System.NullReferenceException is thrown instead

return name ?? throw; does not satisfy this condition as only the throw expression would be allowed here, not a throw statement.

At least that's how I read this.

Is there an opposite to the null coalescing operator? (…in any language?)

There's the null-safe dereferencing operator (?.) in Groovy... I think that's what you're after.

(It's also called the safe navigation operator.)

For example:

homePostcode = person?.homeAddress?.postcode

This will give null if person, person.homeAddress or person.homeAddress.postcode is null.

(This is now available in C# 6.0 but not in earlier versions)

C#: Null coalescing operator using nullable bool giving (what I consider) incorrect result

Take a look at the table here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/#operator-precedence

The operator precedence of the null-coalescing operator ?? is lower than the conditional or operator ||.

This means your code is being evaluated as:

Console.WriteLine(nullableBoolEqualFalse ?? (false || true));

false || true evaluates to true, and nullableBoolEqualFalse ?? true evaluates to nullableBoolEqualFalse which is why you are seeing false in the output.

C# compiling optimizations: null coalescing operator - UPDATED - Reflector's bug?

This looks like a bug in Reflector's C# disassembly.

Starting with this code:

public static string _test;
public static string _setting;

public static string Test_1
{
get { return _test ?? (_setting ?? "default"); }
}

Reflector shows this C# disassembly:

public static string Test_1
{
get
{
return (_test ?? (_setting ?? "default"));
}
}

and the corresponding IL:

.method public hidebysig specialname static string get_Test_1() cil managed
{
.maxstack 8
L_0000: ldsfld string ConsoleApplication1.Program::_test
L_0005: dup
L_0006: brtrue.s L_0017
L_0008: pop
L_0009: ldsfld string ConsoleApplication1.Program::_setting
L_000e: dup
L_000f: brtrue.s L_0017
L_0011: pop
L_0012: ldstr "default"
L_0017: ret
}

I am not an IL expert, but this is my take on it:

  • L_0000:ldsfld pushes _test onto the evaluation stack
  • L_0005:dup copies the value (_test) that is topmost on the evaluation stack and pushes that onto the stack.
  • L_0006:brtrue.s pops the value created by dup off the stack and jumps to L_0017 if it is not null.
  • L_0008:pop at this point, _test is null, so pop that value off the stack.

and it continues to evaluate _setting in a similar fashion, finally returning "default" if _setting is also null.

Now, if we add an assignment into the code like this:

public static string Test_2
{
get { return _test ?? (_test = (_setting ?? "default")); }
}

Reflector shows this C# disassembly:

public static string Test_2
{
get
{
if (_test == null)
{
string text1 = _test;
}
return (_test = _setting ?? "default");
}
}

which is not correct (if _test is not null, instead of returning _test, it assigns _setting or "default" to _test and then returns).

However, the IL dissassembly looks like the IL for Test_1, with a couple of extra instructions at L_0017 and L_0018 to do the assignment.

.method public hidebysig specialname static string get_Test_2() cil managed
{
.maxstack 8
L_0000: ldsfld string ConsoleApplication1.Program::_test
L_0005: dup
L_0006: brtrue.s L_001d
L_0008: pop
L_0009: ldsfld string ConsoleApplication1.Program::_setting
L_000e: dup
L_000f: brtrue.s L_0017
L_0011: pop
L_0012: ldstr "default"
L_0017: dup
L_0018: stsfld string ConsoleApplication1.Program::_test
L_001d: ret
}

Finally, if you copy Reflector's C# dissembly and run it against the original, you'll see it produces different results.

using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
_test = "Test";
Console.WriteLine(Test_2);
Console.WriteLine(Reflector_Test_2);
Console.ReadLine();
}

public static string _test;
public static string _setting;

public static string Test_1
{
get { return _test ?? (_setting ?? "default"); }
}

public static string Test_2
{
get { return _test ?? (_test = (_setting ?? "default")); }
}

public static string Reflector_Test_2
{
get
{
if (_test == null)
{
string text1 = _test;
}
return (_test = _setting ?? "default");
}
}
}
}

Outputs

Test
default

Coalesce operator in C#?

Yup:

tb_myTextBox.Text = o.Member ?? "default";

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator

What does question mark and dot operator ?. mean in C# 6.0?

It's the null conditional operator. It basically means:

"Evaluate the first operand; if that's null, stop, with a result of null. Otherwise, evaluate the second operand (as a member access of the first operand)."

In your example, the point is that if a is null, then a?.PropertyOfA will evaluate to null rather than throwing an exception - it will then compare that null reference with foo (using string's == overload), find they're not equal and execution will go into the body of the if statement.

In other words, it's like this:

string bar = (a == null ? null : a.PropertyOfA);
if (bar != foo)
{
...
}

... except that a is only evaluated once.

Note that this can change the type of the expression, too. For example, consider FileInfo.Length. That's a property of type long, but if you use it with the null conditional operator, you end up with an expression of type long?:

FileInfo fi = ...; // fi could be null
long? length = fi?.Length; // If fi is null, length will be null

Why do i need to put the null-coalescing operator in brackets?

v1 is 0 for the exact reason you mentioned: the null-coalescing operator actually has relatively low precedence. This table shows exactly how low.

So for the first expression, 1 + null is evaluated first, and it evaluates to a null int?, which then coalesces to 0.

Is there a practical use for regular OR (|) and regular AND (&) operator

As you say, these operators don't short-circuit. But that's not all: they're used for bit manipulation. Some examples:

Set the 4th bit:

// 00000010 | 00001000 == 00001010
value = value | (1 << 3);

Clear the 4th bit:

// ~00001000 == 11110111
// 00001010 & 11110111 == 00000010
value = value & ~(1 << 3);

Check if the 4th bit is set:

// 00001010 & 00001000 == 00001000
if ((value & (1 << 3)) != 0)
...

In C#, this is commonly used with flag enums (enum types with the [Flags] attribute applied).

Here's an example from the framework:

[Flags]
public enum FileAttributes
{
ReadOnly = 0x1,
Hidden = 0x2,
System = 0x4,
Directory = 0x10,
Archive = 0x20,
Device = 0x40,
Normal = 0x80,

// and so on...
}

So for instance you could test if a file is hidden using code like:

if ((attributes & FileAttributes.Hidden) != 0)
...


Related Topics



Leave a reply



Submit