এর গুরুত্বের উপর 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 এর উত্তর :
এখানে বেশ কয়েকটি উন্নতি করা যেতে পারে।
- প্রথমত, আমি এর
Func<T, TKey>পরিবর্তে একটি গ্রহণ করতাম Func<T, object>; এটি প্রকৃত keyExtractorনিজেই মান টাইপ কীগুলির বক্সিং রোধ করবে ।
- দ্বিতীয়ত, আমি আসলে একটি
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));
}
}
IEqualityComparer<T>যে পাতারGetHashCodeআউট মাত্র সোজা-আপ নষ্ট হয়ে গেছে।