简体   繁体   中英

Null-coalescing operator in a foreach declaration using C#7

I was looking at this code example in C# 7.0 and I was not sure about what was going on under the hood and the performance of this loop.

foreach (var c in text ?? throw new ArgumentNullException(nameof(text)))
{
    ...
}

My questions:

  1. Does the conditional statement get hit once or multiple times (on each iteration)?
  2. The new syntax looks different, what are the benefits to doing it this way?

In terms of "how foreach works", conditional statement will only be calculated once.

You may want to read more about how foreach loops work in these questions:
How do foreach loops work in C#?
Does foreach evaluate the array at every iteration?

Thanks to Svek for explaining that it is a new C# 7.0 feature, which will be released after Visual Studio 2017 RC:
http://structuredsight.com/2016/09/01/c-7-additions-throw-expressions/

I think that "what are benefits" is a sort of opinion-based question.
In my opinion, it brings nothing good and is just ugly in terms or code readability.
I would recommend using a widely-used common good practice:

if (text == null) // or string.IsNullOrEmpty for strings
    throw new ArgumentNullException(nameof(text));

foreach (var c in text)
{
    // ...
}

Probably, we will see null-coalescing + throw exception usage in a couple years and it will become a new standard :)

You should understand foreach inner code for understanding this C# feature. A right part of expression in foreach statement must implement IEnumerable(<T>) interface, and whole loop is, internally, a simple while , something like this:

// here can be NullReferenceException
var en = text.GetEnumerator();
while(en.MoveNext())
{
    var c = en.Current;
    {
        ...
    }
}

As you can see, there is a point in this code the NRE can occur, so you need to check the enumerable before entire loop or Enumerable extensions class , like this:

if (text.IsNullOrWhitespace())
{
    throw new ArgumentNullException(nameof(text));
}

// while loop here or text.SomeLinqCodeHere()

There are some lines of code here which aren't really unnecessary, adding some entropy without real value. In case of simple foreach it really opinion-based decision about code standards, but the real purpose of this feature is chaining it with other new things in C#7 , like ?. operator , like this:

int? length = customers?.Length ?? throw new ...;
Customer first = customers?[0] ?? throw new ...;  
int? count = customers?[0]?.Orders?.Count() ?? throw new ...;

In such cases throwing the exception is similar to comment at the end of the line of code:

int? length = customers?.Length; // should not be null
Customer first = customers?[0]; // should not be null  
int? count = customers?[0]?.Orders?.Count(); // should not be null

but it adds some strict contract-like rules for your code.

As for the performance for foreach loop with such expressions, as already said, it doesn't suffer as getting the enumerator occurs only once, and before the real loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM