web-dev-qa-db-ja.com

IComparerまたはIEqualityComparerまたは単一メソッドインターフェイスの代わりにラムダ式を渡しますか?

私は偶然、この男がラムダ式をArrayList.Sort(IComparer here)またはIEnumerable.SequenceEqual(IEnumerable list、IEqualityComparer here)に渡して、IComparerまたはIEqualityComparerが予期されていたコードを見たことがあります。

私はそれを見たかどうかはわかりませんが、私はただ夢を見ています。そして、メソッドシグネチャでFunc <>またはデリゲートを受け入れるこれらのコレクションの拡張機能を見つけることができないようです。

そのようなオーバーロード/拡張メソッドはありますか?または、そうでない場合、このようにいじくり回して、単一メソッドのインターフェースが予想される場所でアルゴリズム(デリゲートを読み取る)を渡すことは可能ですか?

更新みなさん、ありがとう。私もそう思っていました。夢見ていたに違いない。変換の書き方を知っています。私はそのようなものを見たのか、それとも見たと思ったのかがわからなかった。

まだ別の更新見て、ここで、そのようなインスタンスが1つ見つかりました。私は結局夢を見ていませんでした。 この男がここでやっていること を見てください。何が得られますか?

そして、別のアップデートがあります:OK、わかりました。男はComparison<T>オーバーロードを使用しています。いいねいいですが、あなたを誤解させがちです。いいね。ありがとう。

55
Water Cooler v2

また、解決策を求めて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();
    }
}
23
AcidJunkie

IComparerを期待しているBase Libraryのほとんどの場合、比較を期待するオーバーロードがあると思うので、それが本当に有用かどうかはよくわかりません...

.Net 4.5では、比較からIComparerを取得するメソッドを追加しました: Comparer.Create

そのため、ラムダを渡してIComparerを取得できます。

22
Xose Lluis

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));
11
Anthony Pegram
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));
6
STO

直接渡すことはできませんが、Func<T,T,int>を除くLambdaComparerクラスを定義し、そのクラスをCompareToで使用することで渡すことができます。

それほど簡潔ではありませんが、Funcでいくつかのクリエイティブな拡張メソッドを使用して短くすることができます。

5
Stephan

私は夢を見る理論に投票します。

オブジェクトが期待される場所に関数を渡すことはできません。System.Delegateの派生物(ラムダ)は、これらのインターフェイスを実装していません。

おそらくあなたが見たのは Converter<TInput, TOutput> デリゲート。ラムダによってモデル化できます。 Array.ConvertAll は、このデリゲートのインスタンスを使用します。

3
codekaizen

これらのメソッドには、インターフェイスではなくデリゲートを受け入れるオーバーロードはありませんが、次のとおりです。

  • 通常、Enumerable.OrderByに渡すデリゲートを使用して、より簡単なソートキーを返すことができます。
  • 同様に、Enumerable.Selectを呼び出す前にEnumerable.SequenceEqualを呼び出すことができます
  • IEqualityComparer<T>の観点からFunc<T, T, bool>を実装するラッパーを書くのは簡単です
  • F#では、この種のインターフェイスをラムダの観点から実装できます:)
3
Tim Robinson

ラムダと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;
    }
}
1
ezolotko