منشئ HashSet مع IEqualityCompare المخصص المحدد بواسطة لامدا؟

StackOverflow https://stackoverflow.com/questions/881438

سؤال

حاليا HashSet<T> المُنشئ الذي يسمح لك بتحديد مقارنة المساواة بنفسك هو HashSet<T>(IEqualityComparer<T> comparer) البناء.أرغب في تعريف EqualityComparer هذا على أنه لامدا.

وجدت هذا بلوق وظيفة التي أنشأت فئة تسمح لك بإنشاء المقارنة الخاصة بك من خلال لامدا ثم إخفاء إنشاء هذه الفئة باستخدام طريقة تمديد للقيام على سبيل المثال باستثناء ().

الآن أود أن أفعل الشيء نفسه ولكن مع المُنشئ.هل من الممكن إنشاء مُنشئ من خلال طريقة الامتداد؟أم أن هناك طريقة أخرى يمكنني من خلالها إنشاء ملف HashSet<T>(Func<T,T,int> comparer)?

--تحديث--
للتوضيح، هذا (مقتطف من) نسخة يدوية لما أحاول تحقيقه:

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)));
هل كانت مفيدة؟

المحلول

لا، لا يمكنك إضافة مُنشئات (حتى مع طرق الامتداد).

على افتراض أن لديك طريقة سحرية للوصول من أ Func<T,T,int> إلى 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);
    }
}

لكن؛أنا متشكك بشأن ما يمكنك فعله مع لامدا من أجل المساواة ...لديك مفهومين للتعبير:التجزئة، والمساواة الحقيقية.كيف ستبدو لامدا الخاصة بك؟إذا كنت تحاول التأجيل إلى خصائص الأطفال، فربما أ 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));
    }
}

نصائح أخرى

مارك على حق.لا توجد طريقة بسيطة لرمز lambda واحد للتعبير عن المعلومات المطلوبة لكل من Equals وGetHashCode.وإذا قمت بتوفير GetHashCode الذي يُرجع تجزئات مختلفة للعناصر "المتساوية"، فسيؤدي ذلك إلى سلوك غير صحيح.

وهنا تنفيذ التسوية الخاصة بي.سيسمح بأي Func عام (مثل مارك، لقد تجاهلت 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);
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top