Синтаксический оператор нечетного возврата
Я знаю, что это может звучать странно, но я даже не знаю, как искать этот синтаксис в Интернете, а также я не уверен, что именно означает.
Итак, я просмотрел некоторый код MoreLINQ, а затем заметил этот метод
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
return _(); IEnumerable<TSource> _()
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
if (knownKeys.Add(keySelector(element)))
yield return element;
}
}
}
Что это за странное выражение возврата? return _();
?
2 ответа
Это C# 7.0, который поддерживает локальные функции....
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new
ArgumentNullException(nameof(source));
if (keySelector == null) throw
new ArgumentNullException(nameof(keySelector));
// This is basically executing _LocalFunction()
return _LocalFunction();
// This is a new inline method,
// return within this is only within scope of
// this method
IEnumerable<TSource> _LocalFunction()
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
if (knownKeys.Add(keySelector(element)))
yield return element;
}
}
}
Текущий C# с Func<T>
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new
ArgumentNullException(nameof(source));
if (keySelector == null) throw
new ArgumentNullException(nameof(keySelector));
Func<IEnumerable<TSource>> func = () => {
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
if (knownKeys.Add(keySelector(element)))
yield return element;
}
};
// This is basically executing func
return func();
}
Хитрость заключается в том, что _() объявляется после его использования, и это прекрасно.
Практическое использование локальных функций
Приведенный выше пример является просто демонстрацией того, как можно использовать встроенный метод, но, скорее всего, если вы собираетесь вызывать метод только один раз, тогда он бесполезен.
Но в приведенном выше примере, как упоминалось в комментариях Фоши и Луана, есть преимущество использования локальной функции. Так как функция с возвращаемым выходом не будет выполняться, пока кто-то не выполнит ее итерацию, в этом случае будет выполнен метод вне локальной функции и будет выполнена проверка параметров, даже если никто не будет выполнять итерацию значения.
Много раз мы повторяли код в методе, давайте посмотрим на этот пример.
public void ValidateCustomer(Customer customer){
if( string.IsNullOrEmpty( customer.FirstName )){
string error = "Firstname cannot be empty";
customer.ValidationErrors.Add(error);
ErrorLogger.Log(error);
throw new ValidationError(error);
}
if( string.IsNullOrEmpty( customer.LastName )){
string error = "Lastname cannot be empty";
customer.ValidationErrors.Add(error);
ErrorLogger.Log(error);
throw new ValidationError(error);
}
... on and on...
}
Я мог бы оптимизировать это с...
public void ValidateCustomer(Customer customer){
void _validate(string value, string error){
if(!string.IsNullOrWhitespace(value)){
// i can easily reference customer here
customer.ValidationErrors.Add(error);
ErrorLogger.Log(error);
throw new ValidationError(error);
}
}
_validate(customer.FirstName, "Firstname cannot be empty");
_validate(customer.LastName, "Lastname cannot be empty");
... on and on...
}
Рассмотрим более простой пример
void Main()
{
Console.WriteLine(Foo()); // Prints 5
}
public static int Foo()
{
return _();
// declare the body of _()
int _()
{
return 5;
}
}
_()
является локальной функцией, объявленной в методе, содержащем оператор return