একটি আইক্যুয়ালি কম্পিউটারের একটি প্রতিনিধি মোড়ানো


127

বেশ কয়েকটি লিনক.আনুনেবল ফাংশন গ্রহণ করে IEqualityComparer<T>। এমন কি কোনও সুবিধাজনক মোড়কের ক্লাস delegate(T,T)=>boolবাস্তবায়নের জন্য গ্রহণ করে IEqualityComparer<T>? এটি একটি লেখার পক্ষে যথেষ্ট সহজ (যদি আপনার সঠিক হ্যাশকোড সংজ্ঞায়িত করতে সমস্যাগুলি উপেক্ষা করা হয়) তবে বাক্সের বাইরে কোনও সমাধান আছে কিনা তা জানতে চাই।

বিশেষত, আমি Dictionaryসদস্য পদ নির্ধারণ করতে শুধুমাত্র কীগুলি ব্যবহার করে (বিভিন্ন বিধি অনুসারে মানগুলি বজায় রেখে) সেট অপারেশন করতে চাই ।

উত্তর:


44

সাধারণত, আমি উত্তরে @ স্যাম মন্তব্য করে সমাধান করতে পেরেছি (আচরণটি পরিবর্তন না করে কিছুটা সাফ করার জন্য মূল পোস্টে কিছু সম্পাদনা করেছি।)

ডিফল্ট হ্যাশিং নীতিটিতে একটি [আইএমএনএসএইচও] সমালোচনামূলক সমাধান সহ নীচে আমার @ স্যামের উত্তরের রিফ রয়েছে :

class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    readonly Func<T, T, bool> _comparer;
    readonly Func<T, int> _hash;

    public FuncEqualityComparer( Func<T, T, bool> comparer )
        : this( comparer, t => 0 ) // NB Cannot assume anything about how e.g., t.GetHashCode() interacts with the comparer's behavior
    {
    }

    public FuncEqualityComparer( Func<T, T, bool> comparer, Func<T, int> hash )
    {
        _comparer = comparer;
        _hash = hash;
    }

    public bool Equals( T x, T y )
    {
        return _comparer( x, y );
    }

    public int GetHashCode( T obj )
    {
        return _hash( obj );
    }
}

5
যতদূর আমি উদ্বিগ্ন এটি সঠিক উত্তর। কোন IEqualityComparer<T>যে পাতার GetHashCodeআউট মাত্র সোজা-আপ নষ্ট হয়ে গেছে।
ড্যান তাও

1
@ জোশুয়া ফ্র্যাঙ্ক: সাম্যকে বোঝাতে হ্যাশ সমতা ব্যবহার করা বৈধ নয় - কেবল বিপরীতটি সত্য। সংক্ষেপে, @ ডান টাও তিনি যা বলেছেন তা পুরোপুরি সঠিক, এবং এই উত্তরটি কেবল পূর্বের অসম্পূর্ণ উত্তরের জন্য এই বাস্তবতার প্রয়োগ
রুবেন বারটেলিংক

2
@ রুবেন বারটেলিংক: স্পষ্ট করার জন্য ধন্যবাদ। তবে আমি এখনও আপনার টি => ০ এর হ্যাশিং নীতিটি বুঝতে পারি না all যদি সমস্ত বস্তু সর্বদা একই জিনিস (শূন্য) এ হ্যাশ করে, তবে @ ড্যান টাও-এর পয়েন্ট অনুসারে, অবজেক্ট.গেটহ্যাশকোড ব্যবহার করার চেয়ে আরও বেশি ভাঙা হয়নি? কেন সবসময় কলারকে একটি ভাল হ্যাশ ফাংশন সরবরাহ করতে বাধ্য করা হয় না?
জোশুয়া ফ্রাঙ্ক

1
সুতরাং এটি ধারণা করা মোটেও যুক্তিযুক্ত নয় যে কোনও ফানকের সরবরাহ করা হয়েছে এমন একটি স্বেচ্ছাসেবী অ্যালগরিদম হ্যাশ কোডগুলি পৃথক হওয়া সত্ত্বেও সম্ভবত সত্যটি ফিরে আসতে পারে না। আপনার বক্তব্য যে সমস্ত সময় শূন্য ফিরে আসা ঠিক হ্যাশিং নয়। এই কারণেই যখন একটি প্রোফাইলার আমাদের জানান যে অনুসন্ধানগুলি যথেষ্ট দক্ষ নয় তখন হ্যাশিং ফানকের জন্য একটি ওভারলোড রয়েছে। এই সমস্ত ক্ষেত্রে একমাত্র পয়েন্ট হ'ল যদি আপনি একটি ডিফল্ট হ্যাশিং অ্যালগরিদম পেতে চলেছেন তবে এটি এমন একটি হওয়া উচিত যা সময়ের 100% কাজ করে এবং বিপজ্জনক অতিমাত্রায় সঠিক আচরণ না করে। এবং তারপরে আমরা পারফরম্যান্সে কাজ করতে পারি!
রুবেন বারটেলিংক

4
অন্য কথায়, যেহেতু আপনি একটি কাস্টম তুলক ব্যবহার করছেন এটির সাথে ডিফল্ট তুলনামূলক সম্পর্কিত অবজেক্টের ডিফল্ট হ্যাশ কোডের কোনও সম্পর্ক নেই, সুতরাং আপনি এটি ব্যবহার করতে পারবেন না।
পিট ব্রিটিস

170

এর গুরুত্বের উপর GetHashCode

অন্যরা ইতিমধ্যে মন্তব্য করেছে যে কোনও কাস্টম IEqualityComparer<T>বাস্তবায়নে আসলেই একটি GetHashCodeপদ্ধতি অন্তর্ভুক্ত করা উচিত ; তবে কেউ কেন কোনও বিশদভাবে তা ব্যাখ্যা করতে বিরক্ত করছে না।

কারণটা এখানে. আপনার প্রশ্নে লিনকিউ এক্সটেনশন পদ্ধতির উল্লেখ রয়েছে; এগুলির প্রায় সবাই সঠিকভাবে কাজ করতে হ্যাশ কোডগুলিতে নির্ভর করে কারণ তারা দক্ষতার জন্য অভ্যন্তরীণভাবে হ্যাশ টেবিলগুলি ব্যবহার করে।

Distinctউদাহরণস্বরূপ, নিন । এই এক্সটেনশন পদ্ধতির প্রভাবগুলি বিবেচনা করুন যদি এটি ব্যবহার করা সমস্তই একটি Equalsপদ্ধতি ছিল। কোনও আইটেম ইতিমধ্যে যদি সিক্যুয়েন্সে স্ক্যান করা হয়েছে তা আপনি কীভাবে নির্ধারণ করবেন Equals? আপনি ইতিমধ্যে দেখেছেন এমন মানগুলির পুরো সংগ্রহটি গণনা করুন এবং ম্যাচের জন্য পরীক্ষা করেছেন। এটি কোনও ও (এন) এর পরিবর্তে Distinctসবচেয়ে খারাপ ক্ষেত্রে O (N 2 ) অ্যালগরিদম ব্যবহারের ফলে তৈরি হবে !

ভাগ্যক্রমে, এটি কেস নয়। শুধু ব্যবহার Distinctকরে না ; এটি পাশাপাশি ব্যবহার করে । প্রকৃতপক্ষে, এটি কোনও সরবরাহ সরবরাহ না করে একেবারে ঠিকভাবে কাজ করে না । নীচে এটি বোঝানো একটি স্বতন্ত্র উদাহরণ।EqualsGetHashCodeIEqualityComparer<T>GetHashCode

বলুন আমার কাছে নিম্নলিখিত ধরণের রয়েছে:

class Value
{
    public string Name { get; private set; }
    public int Number { get; private set; }

    public Value(string name, int number)
    {
        Name = name;
        Number = number;
    }

    public override string ToString()
    {
        return string.Format("{0}: {1}", Name, Number);
    }
}

এখন বলুন আমার একটি আছে List<Value>এবং আমি পৃথক নামের সাথে সমস্ত উপাদান খুঁজে পেতে চাই। এটি Distinctএকটি কাস্টম সমতা তুলনামূলক ব্যবহারের জন্য উপযুক্ত ব্যবহারের কেস । সুতরাং আসুন আকুর উত্তরComparer<T> থেকে ক্লাসটি ব্যবহার করা যাক :

var comparer = new Comparer<Value>((x, y) => x.Name == y.Name);

এখন, আমাদের যদি Valueএকই Nameসম্পত্তি সহ একগুচ্ছ উপাদান রয়েছে , সেগুলি কি Distinctডান দিয়ে ফিরে আসা একটি মূল্যের মধ্যে পড়ে যেতে হবে ? দেখা যাক...

var values = new List<Value>();

var random = new Random();
for (int i = 0; i < 10; ++i)
{
    values.Add("x", random.Next());
}

var distinct = values.Distinct(comparer);

foreach (Value x in distinct)
{
    Console.WriteLine(x);
}

আউটপুট:

x: 1346013431
x: 1388845717
x: 1576754134
x: 1104067189
x: 1144789201
x: 1862076501
x: 1573781440
x: 646797592
x: 655632802
x: 1206819377

হুঁ, এটা কাজ করেনি, তাই না?

কি হবে GroupBy? এর চেষ্টা করুন:

var grouped = values.GroupBy(x => x, comparer);

foreach (IGrouping<Value> g in grouped)
{
    Console.WriteLine("[KEY: '{0}']", g);
    foreach (Value x in g)
    {
        Console.WriteLine(x);
    }
}

আউটপুট:

[কেই = 'x: 1346013431']
x: 1346013431
[কেই = 'x: 1388845717']
x: 1388845717
[কেই = 'x: 1576754134']
x: 1576754134
[কেই = 'x: 1104067189']
x: 1104067189
[কেই = 'x: 1144789201']
x: 1144789201
[কেই = 'x: 1862076501']
x: 1862076501
[কেই = 'x: 1573781440']
x: 1573781440
[কেই = 'x: 646797592']
x: 646797592
[কেই = 'x: 655632802']
x: 655632802
[কেই = 'x: 1206819377']
x: 1206819377

আবার: কাজ হয়নি।

আপনি যদি এটির বিষয়ে চিন্তা করেন তবে অভ্যন্তরীণভাবে Distinctকোনও HashSet<T>(বা সমমানের) GroupByব্যবহার করার জন্য এবং Dictionary<TKey, List<T>>অভ্যন্তরীণ অভ্যন্তরের মতো কিছু ব্যবহার করার জন্য এটি বোধগম্য হবে । এই ব্যাখ্যা করতে পারে কেন এই পদ্ধতিগুলি কাজ করে না? এর চেষ্টা করুন:

var uniqueValues = new HashSet<Value>(values, comparer);

foreach (Value x in uniqueValues)
{
    Console.WriteLine(x);
}

আউটপুট:

x: 1346013431
x: 1388845717
x: 1576754134
x: 1104067189
x: 1144789201
x: 1862076501
x: 1573781440
x: 646797592
x: 655632802
x: 1206819377

হ্যাঁ ... বোঝার জন্য শুরু?

আশা করি এই উদাহরণগুলি থেকে এটি পরিষ্কার হয়ে গেছে যে GetHashCodeকোনও IEqualityComparer<T>বাস্তবায়নের ক্ষেত্রে যথাযথ অন্তর্ভুক্ত করা কেন এত গুরুত্বপূর্ণ।


আসল উত্তর

উপর সম্প্রসারিত orip এর উত্তর :

এখানে বেশ কয়েকটি উন্নতি করা যেতে পারে।

  1. প্রথমত, আমি এর Func<T, TKey>পরিবর্তে একটি গ্রহণ করতাম Func<T, object>; এটি প্রকৃত keyExtractorনিজেই মান টাইপ কীগুলির বক্সিং রোধ করবে ।
  2. দ্বিতীয়ত, আমি আসলে একটি where TKey : IEquatable<TKey>সীমাবদ্ধতা যুক্ত করব; এটি Equalsকলটিতে বক্সিং প্রতিরোধ করবে ( প্যারামিটার object.Equalsলাগে object; এটি বক্সিং না করে প্যারামিটার IEquatable<TKey>নেওয়ার জন্য আপনার একটি প্রয়োগকরণ প্রয়োজন TKey)। স্পষ্টতই এটি খুব তীব্র একটি বিধিনিষেধ সৃষ্টি করতে পারে, সুতরাং আপনি সীমাবদ্ধতা ছাড়াই একটি বেস শ্রেণি তৈরি করতে এবং এর সাথে উত্পন্ন শ্রেণি তৈরি করতে পারেন।

ফলাফলের কোডটি দেখতে কেমন হতে পারে তা এখানে:

public class KeyEqualityComparer<T, TKey> : IEqualityComparer<T>
{
    protected readonly Func<T, TKey> keyExtractor;

    public KeyEqualityComparer(Func<T, TKey> keyExtractor)
    {
        this.keyExtractor = keyExtractor;
    }

    public virtual bool Equals(T x, T y)
    {
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }

    public int GetHashCode(T obj)
    {
        return this.keyExtractor(obj).GetHashCode();
    }
}

public class StrictKeyEqualityComparer<T, TKey> : KeyEqualityComparer<T, TKey>
    where TKey : IEquatable<TKey>
{
    public StrictKeyEqualityComparer(Func<T, TKey> keyExtractor)
        : base(keyExtractor)
    { }

    public override bool Equals(T x, T y)
    {
        // This will use the overload that accepts a TKey parameter
        // instead of an object parameter.
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }
}

1
আপনার StrictKeyEqualityComparer.Equalsপদ্ধতিটি একই হিসাবে উপস্থিত বলে মনে হচ্ছে KeyEqualityComparer.EqualsTKey : IEquatable<TKey>সীমাবদ্ধতা কি TKey.Equalsআলাদাভাবে কাজ করে?
জাস্টিন মরগান

2
@ জাস্টিনমর্গান: হ্যাঁ - প্রথম ক্ষেত্রে যেহেতু TKeyযেকোন স্বেচ্ছাসেবী প্রকার হতে পারে, সংকলক ভার্চুয়াল পদ্ধতিটি ব্যবহার Object.Equalsকরবে যার জন্য মূল্য ধরণের পরামিতিগুলির বক্সিং প্রয়োজন হবে, যেমন int,। পরবর্তী ক্ষেত্রে, তবে, যেহেতু TKeyবাস্তবায়ন করতে সীমাবদ্ধ IEquatable<TKey>তাই TKey.Equalsপদ্ধতিটি ব্যবহার করা হবে যা কোনও বক্সিংয়ের প্রয়োজন হবে না।
ড্যান তাও

2
অত্যন্ত আকর্ষণীয়, তথ্যের জন্য ধন্যবাদ। এই উত্তরগুলি না দেখে গেটহ্যাশকোডের এই লিনকিউ প্রভাব রয়েছে আমার কোনও ধারণা ছিল না। ভবিষ্যতের ব্যবহারের জন্য জেনে দুর্দান্ত।
জাস্টিন মরগান

1
@ জোহনেসএইচ: সম্ভবত! StringKeyEqualityComparer<T, TKey>খুব প্রয়োজন মুছে দিত।
ড্যান তাও

1
+1 @ ড্যানটাও:। নেট-এ সাম্যতার সংজ্ঞা দেওয়ার সময় কেন কখনই হ্যাশ কোডগুলি উপেক্ষা করা উচিত নয়, তার দুর্দান্ত প্রকাশের জন্য বিলেড ধন্যবাদ।
মার্সেলো ক্যান্টোস

118

আপনি যখন সাম্যতা পরীক্ষাটি কাস্টমাইজ করতে চান, 99% সময় আপনি তুলনা করার জন্য কীগুলি নির্ধারণ করতে আগ্রহী, তুলনা নিজেই নয়।

এটি একটি মার্জিত সমাধান হতে পারে (পাইথনের তালিকা সাজানোর পদ্ধতি থেকে ধারণা )।

ব্যবহার:

var foo = new List<string> { "abc", "de", "DE" };

// case-insensitive distinct
var distinct = foo.Distinct(new KeyEqualityComparer<string>( x => x.ToLower() ) );

KeyEqualityComparerশ্রেণী:

public class KeyEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, object> keyExtractor;

    public KeyEqualityComparer(Func<T,object> keyExtractor)
    {
        this.keyExtractor = keyExtractor;
    }

    public bool Equals(T x, T y)
    {
        return this.keyExtractor(x).Equals(this.keyExtractor(y));
    }

    public int GetHashCode(T obj)
    {
        return this.keyExtractor(obj).GetHashCode();
    }
}

3
এটি আকুর উত্তর চেয়ে অনেক ভাল।
এসএলএক্স

অবশ্যই সঠিক পন্থা। আমার মতামত অনুসারে কিছু দু'টি উন্নতি করা যেতে পারে যা আমি নিজের উত্তরে উল্লেখ করেছি mentioned
ড্যান তাও

1
এটি খুব মার্জিত কোড, তবে এটি প্রশ্নের উত্তর দেয় না, যার কারণে আমি পরিবর্তে @ আকুর উত্তর গ্রহণ করেছি। আমি ফানক <টি, টি, টি> বুল> এর জন্য একটি মোড়ক চাইছিলাম এবং চাবিটি বের করার আমার কোনও প্রয়োজন নেই, কারণ আমার অভিধানে কীটি ইতিমধ্যে আলাদা হয়ে গেছে separated
মার্সেলো ক্যান্টোস

6
@ মার্সেলো: ঠিক আছে, আপনি এটি করতে পারেন; তবে সচেতন থাকুন যে আপনি যদি @ আকুর পন্থা অবলম্বন করতে চলেছেন তবে আপনার সত্যিকার অর্থেFunc<T, int> একটি Tমূল্যের জন্য হ্যাশ কোড সরবরাহ করতে একটি যুক্ত করা উচিত (যেমনটি বলা হয়েছে, যেমন, রুবেনের উত্তর )। অন্যথায় আপনি যে IEqualityComparer<T>বাস্তবায়নটি রেখে গেছেন তা বেশ ভেঙে গেছে, বিশেষত লিনকিউ এক্সটেনশন পদ্ধতিতে এর কার্যকারিতা সম্পর্কিত। এটি কেন তা নিয়ে আলোচনার জন্য আমার উত্তর দেখুন।
ড্যান তাও

এটি দুর্দান্ত তবে কীটি নির্বাচন করা হচ্ছে যদি মানটির ধরণ হত তবে অপ্রয়োজনীয় বক্সিং হবে would চাবিটি সংজ্ঞায়িত করার জন্য সম্ভবত একটি কেকে রাখা ভাল।
গ্রাহাম অ্যামব্রোজ

48

আমি ভয় করি যে বাক্সের বাইরে এমন কোনও মোড়ক নেই। তবে এটি তৈরি করা কঠিন নয়:

class Comparer<T>: IEqualityComparer<T>
{
    private readonly Func<T, T, bool> _comparer;

    public Comparer(Func<T, T, bool> comparer)
    {
        if (comparer == null)
            throw new ArgumentNullException("comparer");

        _comparer = comparer;
    }

    public bool Equals(T x, T y)
    {
        return _comparer(x, y);
    }

    public int GetHashCode(T obj)
    {
        return obj.ToString().ToLower().GetHashCode();
    }
}

...

Func<int, int, bool> f = (x, y) => x == y;
var comparer = new Comparer<int>(f);
Console.WriteLine(comparer.Equals(1, 1));
Console.WriteLine(comparer.Equals(1, 2));

1
তবে, গেটহ্যাশকোডের সেই বাস্তবায়ন সম্পর্কে সতর্ক থাকুন। আপনি যদি বাস্তবে এটি কোনও ধরণের হ্যাশ টেবিলটিতে ব্যবহার করতে যাচ্ছেন তবে আপনি কিছুটা আরও শক্তিশালী কিছু চাইবেন।
কোকোপ

46
এই কোড একটি গুরুতর সমস্যা আছে! এমন শ্রেণীর সাথে আসা খুব সহজ যেটিতে দুটি তুলনামূলক অবজেক্ট রয়েছে যা এই তুলনাকারীর বিচারে সমান তবে বিভিন্ন হ্যাশ কোড রয়েছে।
এম্পি

10
এর প্রতিকারের জন্য, শ্রেণীর জন্য আরও একটি সদস্য প্রয়োজন private readonly Func<T, int> _hashCodeResolverযা অবশ্যই কনস্ট্রাক্টরে পাস হতে হবে এবং GetHashCode(...)পদ্ধতিতে ব্যবহার করতে হবে ।
Herzmeister

6
আমি কৌতুহলী কেন আপনি ব্যবহার করছেন obj.ToString().ToLower().GetHashCode()পরিবর্তে obj.GetHashCode()?
জাস্টিন মরগান 20

3
এই কাঠামোর যে জায়গাগুলি IEqualityComparer<T>অবিচ্ছিন্নভাবে পর্দার পিছনে হ্যাশিং ব্যবহার করে (যেমন, লিনকিউর গ্রুপবি, ডিস্টিন্ট, ব্যতীত, যোগদান, ইত্যাদি) এবং হ্যাশিং সম্পর্কিত এমএস চুক্তিটি এই বাস্তবায়নে ভেঙে গেছে। এখানে এমএসের ডকুমেন্টেশন সংক্ষেপে: "সমান পদ্ধতিটি যদি দুটি এবং এক্স এবং ওয়াইয়ের জন্য সত্য দেয় তবে এক্সের জন্য গেটহ্যাশকোড পদ্ধতিতে ফিরে আসা মানটি y এর জন্য প্রাপ্ত মানের সমান হতে হবে তা নিশ্চিত করার জন্য বাস্তবায়ন প্রয়োজন।" দেখুন: এমএসডিএন.মাইক্রোসফটকম
en

22

ড্যান টাওয়ের উত্তর হিসাবে একই, তবে কয়েকটি উন্নতি সহ:

  1. EqualityComparer<>.Defaultপ্রকৃত তুলনা করার উপর নির্ভর করে যাতে structএটি কার্যকর করা মানের মান ( গুলি) এর জন্য বক্সিং এড়াতে পারে IEquatable<>

  2. যেহেতু EqualityComparer<>.Defaultব্যবহৃত হয়েছে এটি বিস্ফোরিত হয় না null.Equals(something)

  3. চারপাশে স্থির মোড়ক সরবরাহ করা হয়েছে IEqualityComparer<>যার চারপাশে তুলনাকারীর উদাহরণ তৈরির জন্য একটি স্থির পদ্ধতি থাকবে - কলিংকে সহজ করে তোলে। তুলনা করা

    Equality<Person>.CreateComparer(p => p.ID);

    সঙ্গে

    new EqualityComparer<Person, int>(p => p.ID);
  4. IEqualityComparer<>কীটির জন্য নির্দিষ্ট করার জন্য একটি ওভারলোড যুক্ত করা হয়েছে ।

শ্রেণী:

public static class Equality<T>
{
    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector)
    {
        return CreateComparer(keySelector, null);
    }

    public static IEqualityComparer<T> CreateComparer<V>(Func<T, V> keySelector, 
                                                         IEqualityComparer<V> comparer)
    {
        return new KeyEqualityComparer<V>(keySelector, comparer);
    }

    class KeyEqualityComparer<V> : IEqualityComparer<T>
    {
        readonly Func<T, V> keySelector;
        readonly IEqualityComparer<V> comparer;

        public KeyEqualityComparer(Func<T, V> keySelector, 
                                   IEqualityComparer<V> comparer)
        {
            if (keySelector == null)
                throw new ArgumentNullException("keySelector");

            this.keySelector = keySelector;
            this.comparer = comparer ?? EqualityComparer<V>.Default;
        }

        public bool Equals(T x, T y)
        {
            return comparer.Equals(keySelector(x), keySelector(y));
        }

        public int GetHashCode(T obj)
        {
            return comparer.GetHashCode(keySelector(obj));
        }
    }
}

আপনি এটি এর মতো ব্যবহার করতে পারেন:

var comparer1 = Equality<Person>.CreateComparer(p => p.ID);
var comparer2 = Equality<Person>.CreateComparer(p => p.Name);
var comparer3 = Equality<Person>.CreateComparer(p => p.Birthday.Year);
var comparer4 = Equality<Person>.CreateComparer(p => p.Name, StringComparer.CurrentCultureIgnoreCase);

ব্যক্তি একটি সাধারণ শ্রেণি:

class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
}

3
একটি বাস্তবায়ন প্রদানের জন্য +1 যা আপনাকে কীটির জন্য একটি তুলনামূলক সরবরাহ করতে দেয়। আরও নমনীয়তা দেওয়ার পাশাপাশি এটি তুলনা এবং হ্যাশ উভয়ের জন্য বক্সিং মানের ধরণের এড়াতে পারে।
ডেভিগিজার

2
এটি এখানে সর্বাধিক শিখরিত উত্তর। আমি পাশাপাশি একটি নাল চেক যোগ। সম্পূর্ণ হয়েছে।
নওফাল

11
public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    readonly Func<T, T, bool> _comparer;
    readonly Func<T, int> _hash;

    public FuncEqualityComparer( Func<T, T, bool> comparer )
        : this( comparer, t => t.GetHashCode())
    {
    }

    public FuncEqualityComparer( Func<T, T, bool> comparer, Func<T, int> hash )
    {
        _comparer = comparer;
        _hash = hash;
    }

    public bool Equals( T x, T y )
    {
        return _comparer( x, y );
    }

    public int GetHashCode( T obj )
    {
        return _hash( obj );
    }
}

এক্সটেনশন সহ: -

public static class SequenceExtensions
{
    public static bool SequenceEqual<T>( this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer )
    {
        return first.SequenceEqual( second, new FuncEqualityComparer<T>( comparer ) );
    }

    public static bool SequenceEqual<T>( this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> comparer, Func<T, int> hash )
    {
        return first.SequenceEqual( second, new FuncEqualityComparer<T>( comparer, hash ) );
    }
}

@ সাম (যিনি আর এই মন্তব্য হিসাবে বিদ্যমান নেই): আচরণ বিন্যাস ছাড়াই কোড সাফ করেছেন (এবং +1 ')। Riff যোগ stackoverflow.com/questions/98033/...
রুবেন Bartelink

6

অরিপের উত্তর দুর্দান্ত।

আরও সহজ করার জন্য এখানে একটি সামান্য এক্সটেনশন পদ্ধতি:

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, object>    keyExtractor)
{
    return list.Distinct(new KeyEqualityComparer<T>(keyExtractor));
}
var distinct = foo.Distinct(x => x.ToLower())

2

আমি আমার নিজের প্রশ্নের উত্তর দিতে যাচ্ছি। ডিকোরিটিসকে সেট হিসাবে বিবেচনা করার জন্য, সহজ পদ্ধতিটি হ'ল ডিক.কি'র জন্য সেট ক্রিয়াকলাপ প্রয়োগ করা, তারপরে এনামিউরেবল.টো ডিকোরিয়াস (...) দিয়ে ডিকোরিয়ানে ফিরে রূপান্তর করা।


2

(জার্মান পাঠ্য) আইক্যুয়ালিটি বাস্তবায়িতকরণ ল্যাম্বডা এক্সপ্রেশনটির সাথে তুলনা করুন নাল মানগুলিকে যত্নশীল করে এবং আইক্যুয়ালিটি কম্পিউটার তৈরি করতে এক্সটেনশন পদ্ধতিগুলি ব্যবহার করে।

একটি লিনক ইউনিয়নে আইকোয়ালিটি কম্পিউটার তৈরি করতে আপনার ঠিক লিখতে হবে

persons1.Union(persons2, person => person.LastName)

তুলক:

public class LambdaEqualityComparer<TSource, TComparable> : IEqualityComparer<TSource>
{
  Func<TSource, TComparable> _keyGetter;

  public LambdaEqualityComparer(Func<TSource, TComparable> keyGetter)
  {
    _keyGetter = keyGetter;
  }

  public bool Equals(TSource x, TSource y)
  {
    if (x == null || y == null) return (x == null && y == null);
    return object.Equals(_keyGetter(x), _keyGetter(y));
  }

  public int GetHashCode(TSource obj)
  {
    if (obj == null) return int.MinValue;
    var k = _keyGetter(obj);
    if (k == null) return int.MaxValue;
    return k.GetHashCode();
  }
}

টাইপ অনুমান সমর্থন করতে আপনাকে একটি এক্সটেনশন পদ্ধতিও যুক্ত করতে হবে

public static class LambdaEqualityComparer
{
       // source1.Union(source2, lambda)
        public static IEnumerable<TSource> Union<TSource, TComparable>(
           this IEnumerable<TSource> source1, 
           IEnumerable<TSource> source2, 
            Func<TSource, TComparable> keySelector)
        {
            return source1.Union(source2, 
               new LambdaEqualityComparer<TSource, TComparable>(keySelector));
       }
   }

1

কেবলমাত্র একটি অপ্টিমাইজেশন: আমরা মূল্য নির্ধারণের পরিবর্তে বাক্সের বাইরে থাকা সমতা কম্পিউটারের ব্যবহার করতে পারি।

এটি বাস্তবায়নকে আরও পরিষ্কার করে তুলবে যেহেতু বাস্তব তুলনা যুক্তিটি এখন গেটহ্যাশকোড () এবং সমান () এ থাকে যা আপনি ইতিমধ্যে ওভারলোড করে ফেলেছেন।

কোডটি এখানে:

public class MyComparer<T> : IEqualityComparer<T> 
{ 
  public bool Equals(T x, T y) 
  { 
    return EqualityComparer<T>.Default.Equals(x, y); 
  } 

  public int GetHashCode(T obj) 
  { 
    return obj.GetHashCode(); 
  } 
} 

আপনার অবজেক্টে গেটহ্যাশকোড () এবং সমান () পদ্ধতিগুলি ওভারলোড করতে ভুলবেন না।

এই পোস্টটি আমাকে সহায়তা করেছে: সি # দুটি জেনেরিক মানের তুলনা করুন

সুশীল


1
বিশেষ দ্রষ্টব্য একই বিষয়ে মন্তব্যে চিহ্নিত যেমন stackoverflow.com/questions/98033/... - CANT obj.GetHashCode () অনুমান জ্ঞান করে তোলে
রুবেন Bartelink

4
আমি এইটির উদ্দেশ্য পাই না। আপনি একটি সমতা তুলনাকারী তৈরি করেছেন যা ডিফল্ট সমতা তুলকের সমান। তাহলে আপনি সরাসরি এটি ব্যবহার করবেন না কেন?
কোডসইনচওস

1

অরিপের উত্তর দুর্দান্ত। অরিপের উত্তরটি প্রসারিত:

আমি মনে করি যে সমাধানটির কীটি "বেনামে টাইপ" স্থানান্তর করতে "এক্সটেনশন পদ্ধতি" ব্যবহার করে।

    public static class Comparer 
    {
      public static IEqualityComparer<T> CreateComparerForElements<T>(this IEnumerable<T> enumerable, Func<T, object> keyExtractor)
      {
        return new KeyEqualityComparer<T>(keyExtractor);
      }
    }

ব্যবহার:

var n = ItemList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList();
n.AddRange(OtherList.Select(s => new { s.Vchr, s.Id, s.Ctr, s.Vendor, s.Description, s.Invoice }).ToList(););
n = n.Distinct(x=>new{Vchr=x.Vchr,Id=x.Id}).ToList();

0
public static Dictionary<TKey, TValue> Distinct<TKey, TValue>(this IEnumerable<TValue> items, Func<TValue, TKey> selector)
  {
     Dictionary<TKey, TValue> result = null;
     ICollection collection = items as ICollection;
     if (collection != null)
        result = new Dictionary<TKey, TValue>(collection.Count);
     else
        result = new Dictionary<TKey, TValue>();
     foreach (TValue item in items)
        result[selector(item)] = item;
     return result;
  }

এটি ল্যাম্বদা সহ এই জাতীয় কোনও সম্পত্তি নির্বাচন করা সম্ভব করে: .Select(y => y.Article).Distinct(x => x.ArticleID);


-2

আমি বিদ্যমান ক্লাস সম্পর্কে জানি না তবে এরকম কিছু:

public class MyComparer<T> : IEqualityComparer<T>
{
  private Func<T, T, bool> _compare;
  MyComparer(Func<T, T, bool> compare)
  {
    _compare = compare;
  }

  public bool Equals(T x, Ty)
  {
    return _compare(x, y);
  }

  public int GetHashCode(T obj)
  {
    return obj.GetHashCode();
  }
}

দ্রষ্টব্য: আমি আসলে এটি এখনও সংকলন এবং চালনা করি নি, তাই টাইপো বা অন্যান্য বাগ থাকতে পারে।


1
বিশেষ দ্রষ্টব্য একই বিষয়ে মন্তব্যে চিহ্নিত যেমন stackoverflow.com/questions/98033/... - CANT obj.GetHashCode () অনুমান জ্ঞান করে তোলে
রুবেন Bartelink
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.