সম

সমানতা মধ্যে C#

5টি অনুশীলনী

সমানতা সম্পর্কে

প্রোগ্রামিং অনুশীলনীটি C#-এ সমতার বেশ কিছু বৈশিষ্ট্য তুলে ধরে:

Object.Equals()

প্রোগ্রামিং অনুশীলনীতে আলোচিত বিষয়গুলো

  • সাধারণ টাইপগুলো (স্ট্রিং আর প্রিমিটিভ) সাধারণত == এবং != দিয়ে সমতার জন্য পরীক্ষা করা হয়। এই টাইপগুলোর সাথেও পাওয়া যায় এমন Equals() মেথড ব্যবহার করার চেয়ে এটাই বেশি প্রচলিত ধরা হয়। তবে Java প্রোগ্রামারদের স্ট্রিং নিয়ে কাজ করার সময় এই বিষয়টি মনে রাখা উচিত যে C#-এ == মান দিয়ে তুলনা করে, কিন্তু তাদের পুরোনো ভাষা Java-তে তা রেফারেন্স দিয়ে তুলনা করে।
  • রেফারেন্স টাইপগুলো (ক্লাসের ইন্সট্যান্স) object থেকে উত্তরাধিকারসূত্রে পাওয়া Equals() মেথড দিয়ে তুলনা করা হয়। যদি সমতা পরীক্ষার উদ্দেশ্য হয় নিশ্চিত করা যে দুটি অবজেক্ট একেবারে একই ইন্সট্যান্স, তাহলে object-এর ইমপ্লিমেন্টেশনের উপর ভরসা করাই যথেষ্ট। তা না হলে object.Equals() ওভাররাইড করতে হবে।
  • যদি আপনি জানেন যে আপনার ক্লাসের সব ইন্সট্যান্স একই জায়গায় তৈরি হয়, যেমন কোনো গেম বা সিমুলেশনের ক্যারেক্টার, তাহলে রেফারেন্স সমতাই যথেষ্ট। তবে সম্ভবত একই বাস্তব-জগতের সত্তার একাধিক ইন্সট্যান্স তৈরি হবে (উদাহরণস্বরূপ, ডেটাবেজ থেকে, ব্যবহারকারীর ইনপুট থেকে, ওয়েব রিকোয়েস্টের মাধ্যমে)। এক্ষেত্রে যে মানগুলো সত্তাটিকে অনন্যভাবে চিহ্নিত করে, সেগুলোর সমতা পরীক্ষা করতে হবে। তাই Equals() ওভাররাইড করা আবশ্যক।
  • ওভাররাইড করা Equals()-এ সাধারণ টাইপের মেম্বারগুলোর জন্য == দিয়ে আর রেফারেন্স টাইপের মেম্বারগুলোর জন্য Equals()-এর রিকার্সিভ কল দিয়ে সমতা পরীক্ষা থাকবে।
class StatusBar
{
    private readonly int width = 200, height = 20;

    public override bool Equals(object other)
    {
        // ... null and type checks and performance optimisations
        return width == (other as StatusBar).width && height == (other as StatusBar).height;
    }
}
class Window
{
    private readonly string title = "Main";
    private readonly StatusBar statusBar = new StatusBar();

    public override bool Equals(object other)
    {
        // ... null and type checks and performance optimisations
        return title == (other as Window).title && statusBar.Equals((other as Window).statusBar);
    }
}

  • স্ট্যাটিক মেথড object.ReferenceEquals() দুটি অবজেক্ট তুলনা করে দেখতে ব্যবহৃত হয় যে তারা এক ও অভিন্ন ইন্সট্যান্স কি না। এটি স্পষ্টতা আনে এবং যেখানে Equals() ও == ওভাররাইড/ওভারলোড করা হয়েছে সেখানে এটি অপরিহার্য।
var winA = new Window(); // above code shows that all windows are equal
var winB = new Window();
ReferenceEquals(winA, winB);
// => false
var winC = winA;
ReferenceEquals(winA, winC);
// => true

সহায়ক বিষয়

  • public override bool Equals(object obj) ছাড়াও IDE-গুলো সাধারণত protected bool Equals(FacialFeatures other) ওভারলোডটি তৈরি করে, যা উত্তরাধিকার জড়িত থাকলে ব্যবহারের জন্য। একটি ডেরাইভড ক্লাস বেস ক্লাসের Equals() কল করে তারপর নিজের পরীক্ষা যোগ করতে পারে।
  • == ব্যবহার করবেন না, যদি না আপনি আপনার ক্লাসে Equals() মেথডের সাথে সাথে == অপারেটরও ওভারলোড করে থাকেন (operator-overloading অনুশীলনীটি দেখুন), অথবা আপনার কাছে শুধু রেফারেন্সগুলো সমান হলেই চলে।
  • structs-এর সমতা পরীক্ষা নিয়ে আলোচনা করা হয়েছে structs অনুশীলনীতে।
  • tuples-এর সমতা নিয়ে আলোচনা করা হয়েছে tuples অনুশীলনীতে।
  • অনেক ডেভেলপার সমতা-মেথডের ইমপ্লিমেন্টেশন নিজেদের IDE-এর উপর ছেড়ে দেন, কারণ এগুলো সমতার সব সূক্ষ্ম বিষয় সামলে দেয়। উদাহরণস্বরূপ, JetBrains-এর RIDER (v 2020.1) একটি ক্লাসের জন্য নিচের সমতা-মেথডগুলো তৈরি করে:
protected bool Equals(T other)
{
    return field1 == other.field1 && field2.Equals(other.field2);
}

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    if (obj.GetType() != this.GetType()) return false;
    return Equals((T) obj);
}
  • আপনার IDE যে কোড তৈরি করে তা উন্নত করার সিদ্ধান্ত নিলে সাবধান থাকুন। ওই কোড সাধারণত null আর ভুল টাইপের অবজেক্টের বিরুদ্ধে পুরোপুরি সুরক্ষিত এবং বেশ ভালোভাবে অপ্টিমাইজ করা।
  • ডেলিগেট-এর সমতা পরীক্ষা নিয়ে অনুশীলনীতে আলাদাভাবে আলোচনা করা হয়নি।
  • অ্যারে বা বেশিরভাগ কালেকশনের জন্য তৈরি-বানানো সমতা পরীক্ষা নেই। LINQ (পরবর্তী অনুশীলনীগুলোতে আলোচিত) SequenceEquals() সরবরাহ করে, কিন্তু LINQ না থাকলে দুটি কালেকশনের ভেতর দিয়ে ইটারেশন করে একেকটি আইটেম আলাদা আলাদাভাবে তুলনা করা ছাড়া উপায় নেই।
  • নিজের ক্লাসের সাথে == ও != কীভাবে ব্যবহার করবেন, তা নিয়ে আলোচনার জন্য operator-overloading অনুশীলনীটি দেখুন।

Object.GetHashCode()

  • object.GetHashCode() একটি ৩২ বিটের ইন্টিজারের রূপে হ্যাশ কোড রিটার্ন করে। Dictionary<T> ও HashSet<T>-এর মতো ডিকশনারি ও সেট ক্লাস হ্যাশ কোড ব্যবহার করে অবজেক্ট দ্রুতগতিতে সংরক্ষণ ও পুনরুদ্ধার করে। ডিকশনারির ক্ষেত্রে হ্যাশিং কী (key)-এর সাথে সম্পর্কিত।
  • C# ডেভেলপারদের মধ্যে একটি প্রত্যাশা আছে যে আপনি যদি Equals() ওভাররাইড করেন, তাহলে GetHashCode()-ও ওভাররাইড করবেন। Equals() ও GetHashCode()-এর মধ্যে এমন একটি সম্পর্ক আছে যা ডিকশনারি ও হ্যাশ সেট ক্লাস এবং হ্যাশ কোড ব্যবহার করে এমন অন্য সব ক্লাসের সঠিক আচরণের জন্য সত্য হতে হবে। আপনার কাছ থেকে প্রত্যাশা করা হয় যে আপনি মেথডটি এমনভাবে ইমপ্লিমেন্ট করবেন যাতে ভবিষ্যতে হ্যাশ কোড-ভিত্তিক কোনো কালেকশন যোগ করা মেইনটেইনারদের জন্য কোনো ফাঁদ তৈরি না হয়।
  • হ্যাশ কোড আর সমতার সম্পর্ক হলো, যদি দুটি অবজেক্ট সমান হয় (Equal() ট্রু রিটার্ন করে) তাহলে ওই দুটি অবজেক্টের GetHashCode() অবশ্যই একই মান রিটার্ন করবে। এর উল্টো দিকটি এখানে প্রযোজ্য নয়। এটি প্রতিসম নয়। একটি লুকআপ ফাংশন কল্পনা করুন যা প্রথমে হ্যাশ কোডের ভিত্তিতে একটি "বাকেট"-এ যায়, তারপর সমতা পরীক্ষা ব্যবহার করে নির্দিষ্ট আইটেমটি বেছে নেয়।
  • হ্যাশকোড তৈরির সবচেয়ে সহজ উপায় হলো HashCode.Combine() কল করা এবং সমতা পরীক্ষায় ব্যবহৃত মানগুলো (বা তার একটি উপসেট) পাস করা। মনে রাখবেন, Combine()-কে যত বেশি তথ্য দেবেন, হ্যাশ ইমপ্লিমেন্টেশন তত বেশি পারফরম্যান্ট হওয়ার সম্ভাবনা থাকে।
public class Assessment
{
    private int rating;
    private Person boss;

    public override int GetHashCode()
    {
        return HashCode.Combine(rating, boss);
    }
}
  • হ্যাশড কালেকশনটি ব্যবহৃত হওয়ার সময় সমতা পরীক্ষায় ব্যবহৃত মানগুলো স্থির থাকতে হবে। যদি আপনি একটি নির্দিষ্ট মান-সেট দিয়ে কালেকশনে একটি অবজেক্ট যোগ করেন আর পরে সেই মানগুলো বদলে ফেলেন, তাহলে হ্যাশ কোড আর সঠিক "বাকেট" নির্দেশ করবে না। বাস্তবে এর অর্থ হলো অবজেক্টটি ইমিউটেবল হওয়া উচিত। অন্য পদ্ধতিগুলো মেইনটেইনারদের জন্য ঝামেলা তৈরি করার ঝুঁকি রাখে। ইমিউটেবিলিটি নিয়ে অন্য অনুশীলনীগুলোতে আলোচনা করা হয়েছে।
  • সম্ভবত আপনি লাইব্রেরির রুটিনগুলো যার চেয়ে ভালো হ্যাশকোড বানাতে পারবেন, তবে সেটা হয় ডেটার বৈশিষ্ট্য সম্পর্কে আপনার বিস্তারিত ধারণার কারণে, নয়তো এটি এমন একটি খুব সাধারণ কালেকশন যেখানে মান হ্যাশ না করেই সরাসরি ব্যবহার করা যায়। বাড়তি পরিশ্রমটা হয়তো সার্থক নাও হতে পারে।

পারফরম্যান্স উন্নতি

পারফরম্যান্স সামান্য বাড়াতে, বিশেষত যখন অবজেক্টগুলো কোনো কালেকশনের অন্তর্গত, তখন একটি ওভারলোডেড মেম্বার public bool Equals(T other) যোগ করতে পারেন।

এটি রেফারেন্স টাইপের জন্য কিছুটা টাইপ চেকিং বাঁচাবে এবং ভ্যালু টাইপের জন্য একটি বক্সিং ধাপ বাঁচাবে, কারণ public override bool Equals(object other)-এর আর্গুমেন্ট হিসেবে সেগুলোকে আর একটি অবজেক্টে রূপান্তর (বক্স) করতে হবে না।

আপনি যদি আপনার ক্লাসে IEquatable<T> ইন্টারফেস যোগ করেন, তাহলে এই ওভারলোডটি ইমপ্লিমেন্ট করা আবশ্যক হবে। যদি আপনার কোডে এমন রুটিন না থাকে যা IEquatable<T> টাইপের অবজেক্ট নেয় (আর সম্ভবত ইমপ্লিমেন্টিং ক্লাস নির্বিশেষে সমতা নিয়ে মজার কিছু করে), তাহলে ইন্টারফেসটি অন্তর্ভুক্ত করার সত্যিই আর কোনো বাধ্যতামূলক কারণ নেই।

IEqualityComparer<T>

যদি এমন একটি ক্লাস থাকে যা দুটি ভিন্ন উপায়ে অনন্যভাবে চিহ্নিত করা যায়, ধরা যাক একটি Person ক্লাস যার একটি SSID আর একটি অনন্য ইমেইল আছে, তাহলে .NET এমন একটি উপায় দেয় যাতে দুটি ভিন্ন কালেকশন ভিন্ন হ্যাশ-কোড ও সমতা পরীক্ষা ব্যবহার করতে পারে। প্রত্যেকটির নিজস্ব Equals() ও GetHashCode() মেথডসহ IEqualityComparer<T>-এর ভিন্ন ইমপ্লিমেন্টেশন থাকতে পারে। আপনি একটি ডিকশনারি রাখতে পারেন যার কী (key) হলো SSID, আর আরেকটি যার কী (key) হলো ইমেইল।

যেখানে IEqualityComparer<T> ব্যবহৃত হচ্ছে, সেখানে সাধারণত কালেকশন ক্লাসের বাইরে সমস্যা এড়াতে আপনি আপনার আইটেম ক্লাসে Equals() ও GetHashCode() তবুও ওভাররাইড করবেন।

IEqualityComparer<T> ব্যবহার করার সময় একটি বিষয় বিবেচনায় রাখতে হবে যে প্রাইভেট মেথড ইত্যাদি সমতা পরীক্ষার জন্য পাওয়া যাবে না।

যদি কেবল একটি হ্যাশড কালেকশন ব্যবহৃত হয়, তাহলে IEqualityComparer<T> এড়িয়ে সমতা ও হ্যাশ কোড অবজেক্টের ভেতরেই এনক্যাপসুলেট করা ভালো হতে পারে। প্রথমেই এটি আদর্শ নয় যে অবজেক্ট আর হ্যাশড কালেকশনের মধ্যে সম্পর্কের মতো একটি গুরুত্বপূর্ণ নির্ভরতা কম্পাইলার দিয়ে নিশ্চিত করা যায় না, কিন্তু হ্যাশিং ও তুলনার লজিক অন্য জায়গায় থাকলে একজন মেইনটেইনারের জন্য কালেকশনের পরিণতি না ভেবেই ক্লাসের মেম্বার বদলে ফেলা আরও সহজ হয়ে যায়।

ফ্লোটিং-পয়েন্ট সমতা নিয়ে টীকা

অসতর্ক কোডারকে চ্যালেঞ্জ করতে পারে এমন একটি প্রিমিটিভ হলো ফ্লোটিং-পয়েন্ট মানের সমতা পরীক্ষা করা। এটি floating-point-numbers কনসেপ্টের about.md ডকুমেন্টে আলোচনা করা হয়েছে।

সমতা ও উত্তরাধিকার

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

GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি নতুন একটি উইন্ডো বা ট্যাবে খুলবে

সমানতা শিখুন

অনুশীলন লক করা আছে

সমানতা অনুশীলন করতে আরও 4টি অনুশীলনী আনলক করুন