これは奇妙に聞こえるかもしれませんが、インターネットでこの構文を検索する方法すら知らず、正確に何を意味するのかもわかりません。
だから私はいくつかの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ステートメントとは何ですか? return _();
?
これは、ローカル関数をサポートする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;
}
}
}
Func<T>
を使用した現在のC#
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();
}
トリックは、_()が使用後に宣言されることです。これはまったく問題ありません。
ローカル関数の実際の使用
上記の例は、インラインメソッドの使用方法のデモにすぎませんが、メソッドを1回だけ呼び出す場合はほとんど役に立ちません。
しかし、上記の例では、PhoshiおよびLuaanによるコメントで述べたように、ローカル関数を使用する利点があります。 yield returnを使用した関数は、誰かが反復しない限り実行されないため、この場合、ローカル関数の外側のメソッドが実行され、誰も値を反復しなくてもパラメーター検証が実行されます。
メソッドで何度もコードを繰り返してきたので、この例を見てみましょう。
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ステートメントを含むメソッド内で宣言されたローカル関数です。