6

現在、HashSet<T>等値比較を自分で定義できるコンストラクターはHashSet<T>(IEqualityComparer<T> comparer)コンストラクターです。この EqualityComparer をラムダとして定義したいと思います。

ラムダを介して比較子を生成できるクラスを作成した このブログ投稿を見つけ、Extended() などの拡張メソッドを使用してこのクラスの構築を非表示にしています。

今、同じことをしたいのですが、コンストラクターを使用します。拡張メソッドを介してコンストラクターを作成することは可能ですか? または、どういうわけか作成できる別の方法はありHashSet<T>(Func<T,T,int> comparer)ますか?

--UPDATE--
明確にするために、これは私が達成しようとしているもののフリーハンド バージョン (のスニペット) です。

HashSet<FileInfo> resultFiles = new HashSet<FileInfo>(
    srcPath.GetFiles(),
    new LambdaComparer<FileInfo>(
        (f1, f2) => f1.Name.SubString(10).Equals(f2.Name.SubString(10))));

またはより理想的に

HashSet<FileInfo> resultFiles = new HashSet<FileInfo>(
    srcPath.GetFiles(),
    (f1, f2) => f1.Name.SubString(10).Equals(f2.Name.SubString(10)));
4

2 に答える 2

5

いいえ、コンストラクターを追加することはできません (拡張メソッドを使用しても)。

Func<T,T,int>aから anに到達するための魔法の方法があると仮定するとIEqualityComparer<T>(引用できる場合は、そのブログ投稿を読んでみたいと思います)、最も近い方法はおそらく次のようなものです。

public static class HashSet {
    public static HashSet<T> Create<T>(Func<T, T, int> func) {
        IEqualityComparer<T> comparer = YourMagicFunction(func);
        return new HashSet<T>(comparer);
    }
}

でも; 平等のためにラムダで何ができるかについては疑わしいです...表現する2つの概念があります:ハッシュと真の平等です。あなたのラムダはどのように見えますか?子プロパティを延期しようとしている場合は、おそらくFunc<T,TValue>プロパティを選択し、EqualityComparer<TValue>.Default内部で使用します...次のようなものです:

class Person {
    public string Name { get; set; }
    static void Main() {
        HashSet<Person> people = HashSetHelper<Person>.Create(p => p.Name);
        people.Add(new Person { Name = "Fred" });
        people.Add(new Person { Name = "Jo" });
        people.Add(new Person { Name = "Fred" });
        Console.WriteLine(people.Count);
    }
}
public static class HashSetHelper<T> {
    class Wrapper<TValue> : IEqualityComparer<T> {
        private readonly Func<T, TValue> func;
        private readonly IEqualityComparer<TValue> comparer;
        public Wrapper(Func<T, TValue> func,
            IEqualityComparer<TValue> comparer) {
            this.func = func;
            this.comparer = comparer ?? EqualityComparer<TValue>.Default;
        }
        public bool Equals(T x, T y) {
            return comparer.Equals(func(x), func(y));
        }

        public int GetHashCode(T obj) {
            return comparer.GetHashCode(func(obj));
        }
    }
    public static HashSet<T> Create<TValue>(Func<T, TValue> func) {
        return new HashSet<T>(new Wrapper<TValue>(func, null));
    }
    public static HashSet<T> Create<TValue>(Func<T, TValue> func,
        IEqualityComparer<TValue> comparer)
    {
        return new HashSet<T>(new Wrapper<TValue>(func, comparer));
    }
}
于 2009-05-19T07:52:30.653 に答える
1

マークは正しいです。Equals と GetHashCode の両方に必要な情報を単一のラムダで表現する簡単な方法はありません。また、「等しい」要素に対して異なるハッシュを返す GetHashCode を指定すると、正しくない動作が発生します。

これが私の妥協の実装です。それは任意の一般的な Func (Marc のように、あなたが説明しなかったので int を無視しました) を許可し、(契約に準拠しているという点で) 正しいですが、非常に非効率的な動作をします。

ニーズに合った本物の IEqualityComparer を使い続けることをお勧めします。ただし、C# が匿名の内部クラスをサポートしていないのは残念です。

public static class HashSetDelegate
{
    public static HashSet<T> Create<T>(Func<T, T, bool> func)
    {
    return new HashSet<T>(new FuncIEqualityComparerAdapter<T>(func));
    }

    private class FuncIEqualityComparerAdapter<U> : IEqualityComparer<U>
    {
    private Func<U, U, bool> func;
    public FuncIEqualityComparerAdapter(Func<U, U, bool> func)
    {
        this.func = func;
    }

    public bool Equals(U a, U b)
    {
        return func(a, b);
    }

    public int GetHashCode(U obj)
    {
        return 0;
    }  

    }
}

public class HashSetTest
{
    public static void Main()
    {
    HashSet<string> s = HashSetDelegate.Create((string a, string b) => string.Compare(a, b, true) == 0);
    }
}
于 2009-05-19T08:49:29.803 に答える