Closed teejay-87 closed 6 years ago
I saw it is quite spread over the whole library. Honestly, can't understand why.
Note that the local function can have access to the local variables and also the argument of the enclosing method, so you do not have the work of pass the arguments to the private function (iterator block) as pre-c# 7.
Also, one of the major benefits of local functions is encapsulation -- a local function can be called only from its enclosing type. Note that if you have a private method in your class, any member of the class can invoke the private method.
@teejay-87 the benefit is that the processing done before the return (and the arguments checking they include) are performed as soon as the method is invoked, whereas the instructions of the local function are only executed at the time the returned IEnumerable<>
is iterated over. An example might make this clearer. With C# 6, to achieve the same, one needs:
public static IEnumerable<T> Operator<T>(this IEnumerable<T> src) {
if (src == null) throw new ArgumentNullException(nameof(src));
return OperatorImpl(src);
}
private static IEnumerable<T> OperatorImpl<T>(this IEnumerable<T> src) {
// Note there is no null check
foreach(var i in src) yield return i; // statements
}
The problem with this is that it creates an unnecessary private function that pollutes the internal namespace (ie, OperatorImpl
is callable by Operator2
which it shouldn't). With the C#7 version:
public static IEnumerable<T> Operator<T>(this IEnumerable<T> src) {
if (src == null) throw new ArgumentNullException(nameof(src));
return _(); IEnumerable<T> _() {
// Note there is no null check
foreach(var i in src) yield return i; // statements
}
}
Now the implementation is not reachable to other operators, thus increasing encapsulation.
In my opinion you simply DON'T NEED any function.
You can write the statements contained in that function directly in the main method.
The argument checking is done at method call time. The rest is executed while enumerating. That's the purpose of yield return.
In fact, when you write () you are immediately calling and thus executing the function.
The deferrability comes into play ONLY when yield return is called.
@teejay-87 yes you do. Iterator functions are evaluated lazily. This means they don't actually execute until you enumerate the results:
public static IEnumerable<T> Operator<T>(this IEnumerable<T> src) {
if (src == null) throw new ArgumentNullException(nameof(src));
foreach(var i in src) yield return i; // statements
}
var res = Operator(null); // No exception here!
foreach(var item in res) {} // Exception here!
I'm going to close this issue now, as it does not appear to be an issue with MoreLINQ.
I'm sorry.
I really ignored that
When you use the
yield
keyword in a statement, you indicate that the method, operator, orget
accessor in which it appears is an iterator.
So you used two methods to split condition checking and iteration.
What's the purpose of returning the execution of the local function
_
in MoreLINQ/MoreLinq/Batch.cs ?return _(); IEnumerable<TResult> _() { // statements... }
It is not backward compatible with C# 6.0 and I see no benefits over directly executing
// statements...
.