大文字と小文字を区別しない動的なGroupBy実装を作成したい。 Expression.Call
を使用しています。これにより、式を引数として渡すことができます。
カスタムコンパレータを作成する方法についてはいくつかの回答がありますが、この質問は、コンパレータを動的に渡す方法についてです。
ここに完全な方法があります:
public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (keySelector == null) throw new ArgumentNullException("keySelector");
if (elementSelector == null) throw new ArgumentNullException("elementSelector");
LambdaExpression keyLambda = DynamicExpression.ParseLambda(source.ElementType, null, keySelector, false, values);
LambdaExpression elementLambda = DynamicExpression.ParseLambda(source.ElementType, null, elementSelector, false, values);
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"GroupBy",
new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
source.Expression,
Expression.Quote(keyLambda),
Expression.Quote(elementLambda)
)
);
}
Queryable.GroupBy
への呼び出しは、以下Queryable.GroupBy
作成されQueryable.GroupBy
。
Expression.Call(typeof(Queryable), "GroupBy",
new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
source.Expression, Expression.Quote(keyLambda), Expression.Quote(elementLambda))
Queryable.GroupBy
では、カスタムIEqualityComparer
を渡すことができQueryable.GroupBy
。これどうやってするの? Expression.Call
では、 Expression
型の引数のみを渡すことができます。
キーのGetHashCode()
を動的にオーバーライドするなど、大文字と小文字を区別せずにグループ化できる他の方法はありますか?
タイプが動的で文字列ではないため、 StringComparer
はここでは使用できません。うまくいった解決策を見つけるために、クシシュトフスの回答について詳しく説明する必要がありました。
最初に、 keyLambda.Body.Type
と同じタイプのカスタム動的比較子DynamicCaseInsensitiveComparer<T>
(IEqualityComparerを実装)のインスタンスを作成します。タイプは変数によって提供されるため、 MakeGenericType
を使用するMakeGenericType
ます。次に、それをGroupBy呼び出しに追加します。
var comparerType = typeof(DynamicCaseInsensitiveComparer<>).MakeGenericType(keyLambda.Body.Type);
var keyComparer = Activator.CreateInstance(comparerType);
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"GroupBy",
new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
source.Expression,
Expression.Quote(keyLambda),
Expression.Quote(elementLambda),
Expression.Constant(keyComparer)
)
);
カスタム比較を作成する方法は他の質問で回答されています。たとえば、匿名タイプのIEqualityComparerを参照してください
通常はGroupBy
呼び出しで比較子を追加するのと同じ方法で呼び出す必要があります。
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"GroupBy",
new Type[] { source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type },
source.Expression,
Expression.Quote(keyLambda),
Expression.Quote(elementLambda),
Expression.Constant(StringComparer.InvariantCultureIgnoreCase)
)
);