私は偶然、この男がラムダ式をArrayList.Sort(IComparer here)またはIEnumerable.SequenceEqual(IEnumerable list、IEqualityComparer here)に渡して、IComparerまたはIEqualityComparerが予期されていたコードを見たことがあります。
私はそれを見たかどうかはわかりませんが、私はただ夢を見ています。そして、メソッドシグネチャでFunc <>またはデリゲートを受け入れるこれらのコレクションの拡張機能を見つけることができないようです。
そのようなオーバーロード/拡張メソッドはありますか?または、そうでない場合、このようにいじくり回して、単一メソッドのインターフェースが予想される場所でアルゴリズム(デリゲートを読み取る)を渡すことは可能ですか?
更新みなさん、ありがとう。私もそう思っていました。夢見ていたに違いない。変換の書き方を知っています。私はそのようなものを見たのか、それとも見たと思ったのかがわからなかった。
まだ別の更新見て、ここで、そのようなインスタンスが1つ見つかりました。私は結局夢を見ていませんでした。 この男がここでやっていること を見てください。何が得られますか?
そして、別のアップデートがあります:OK、わかりました。男はComparison<T>
オーバーロードを使用しています。いいねいいですが、あなたを誤解させがちです。いいね。ありがとう。
また、解決策を求めてWebをグーグルで検索していましたが、満足できるものは見つかりませんでした。だから私は一般的なEqualityComparerFactoryを作成しました:
using System;
using System.Collections.Generic;
/// <summary>
/// Utility class for creating <see cref="IEqualityComparer{T}"/> instances
/// from Lambda expressions.
/// </summary>
public static class EqualityComparerFactory
{
/// <summary>Creates the specified <see cref="IEqualityComparer{T}" />.</summary>
/// <typeparam name="T">The type to compare.</typeparam>
/// <param name="getHashCode">The get hash code delegate.</param>
/// <param name="equals">The equals delegate.</param>
/// <returns>An instance of <see cref="IEqualityComparer{T}" />.</returns>
public static IEqualityComparer<T> Create<T>(
Func<T, int> getHashCode,
Func<T, T, bool> equals)
{
if (getHashCode == null)
{
throw new ArgumentNullException(nameof(getHashCode));
}
if (equals == null)
{
throw new ArgumentNullException(nameof(equals));
}
return new Comparer<T>(getHashCode, equals);
}
private class Comparer<T> : IEqualityComparer<T>
{
private readonly Func<T, int> _getHashCode;
private readonly Func<T, T, bool> _equals;
public Comparer(Func<T, int> getHashCode, Func<T, T, bool> equals)
{
_getHashCode = getHashCode;
_equals = equals;
}
public bool Equals(T x, T y) => _equals(x, y);
public int GetHashCode(T obj) => _getHashCode(obj);
}
}
CreateComparerメソッドは、GetHashCode(T)へのデリゲートとEquals(T、T)へのデリゲートという2つの引数を取るという考え方です。
例:
class Person
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
}
class Program
{
static void Main(string[] args)
{
var list1 = new List<Person>(new[]{
new Person { Id = 1, FirstName = "Walter", LastName = "White" },
new Person { Id = 2, FirstName = "Jesse", LastName = "Pinkman" },
new Person { Id = 3, FirstName = "Skyler", LastName = "White" },
new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" },
});
var list2 = new List<Person>(new[]{
new Person { Id = 1, FirstName = "Walter", LastName = "White" },
new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" },
});
// We're comparing based on the Id property
var comparer = EqualityComparerFactory.Create<Person>(
a => a.Id.GetHashCode(),
(a, b) => a.Id==b.Id);
var intersection = list1.Intersect(list2, comparer).ToList();
}
}
IComparerを期待しているBase Libraryのほとんどの場合、比較を期待するオーバーロードがあると思うので、それが本当に有用かどうかはよくわかりません...
.Net 4.5では、比較からIComparerを取得するメソッドを追加しました: Comparer.Create
そのため、ラムダを渡してIComparerを取得できます。
Array.Sortメソッドにラムダを提供できます。これは、T型の2つのオブジェクトを受け入れて整数を返すメソッドが必要なためです。そのため、次の定義のラムダを提供できます(a, b) => a.CompareTo(b)
。整数配列の降順ソートを行う例:
int[] array = { 1, 8, 19, 4 };
// descending sort
Array.Sort(array, (a, b) => -1 * a.CompareTo(b));
public class Comparer2<T, TKey> : IComparer<T>, IEqualityComparer<T>
{
private readonly Expression<Func<T, TKey>> _KeyExpr;
private readonly Func<T, TKey> _CompiledFunc
// Constructor
public Comparer2(Expression<Func<T, TKey>> getKey)
{
_KeyExpr = getKey;
_CompiledFunc = _KeyExpr.Compile();
}
public int Compare(T obj1, T obj2)
{
return Comparer<TKey>.Default.Compare(_CompiledFunc(obj1), _CompiledFunc(obj2));
}
public bool Equals(T obj1, T obj2)
{
return EqualityComparer<TKey>.Default.Equals(_CompiledFunc(obj1), _CompiledFunc(obj2));
}
public int GetHashCode(T obj)
{
return EqualityComparer<TKey>.Default.GetHashCode(_CompiledFunc(obj));
}
}
このように使用します
ArrayList.Sort(new Comparer2<Product, string>(p => p.Name));
直接渡すことはできませんが、Func<T,T,int>
を除くLambdaComparer
クラスを定義し、そのクラスをCompareTo
で使用することで渡すことができます。
それほど簡潔ではありませんが、Func
でいくつかのクリエイティブな拡張メソッドを使用して短くすることができます。
私は夢を見る理論に投票します。
オブジェクトが期待される場所に関数を渡すことはできません。System.Delegateの派生物(ラムダ)は、これらのインターフェイスを実装していません。
おそらくあなたが見たのは Converter<TInput, TOutput>
デリゲート。ラムダによってモデル化できます。 Array.ConvertAll は、このデリゲートのインスタンスを使用します。
これらのメソッドには、インターフェイスではなくデリゲートを受け入れるオーバーロードはありませんが、次のとおりです。
Enumerable.OrderBy
に渡すデリゲートを使用して、より簡単なソートキーを返すことができます。Enumerable.Select
を呼び出す前にEnumerable.SequenceEqual
を呼び出すことができますIEqualityComparer<T>
の観点からFunc<T, T, bool>
を実装するラッパーを書くのは簡単ですラムダと2つの異なる要素タイプで使用するためにこの関数が必要な場合:
static class IEnumerableExtensions
{
public static bool SequenceEqual<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, bool> comparer)
{
if (first == null)
throw new NullReferenceException("first");
if (second == null)
throw new NullReferenceException("second");
using (IEnumerator<T1> e1 = first.GetEnumerator())
using (IEnumerator<T2> e2 = second.GetEnumerator())
{
while (e1.MoveNext())
{
if (!(e2.MoveNext() && comparer(e1.Current, e2.Current)))
return false;
}
if (e2.MoveNext())
return false;
}
return true;
}
}