সি # তে টেমপ্লেট বিশেষায়িতকরণ কীভাবে করবেন


84

আপনি কীভাবে সি # তে বিশেষীকরণ করবেন?

আমি একটি সমস্যা ডেকে আনব। আপনার একটি টেম্পলেট টাইপ রয়েছে, এটি কী তা আপনার কোনও ধারণা নেই। তবে আপনি কি জানেন যে এটি XYZকল করতে চাইলে তা এসেছে .alternativeFunc()। একটি দুর্দান্ত উপায় হ'ল একটি বিশেষায়িত ফাংশন বা ক্লাস কল করা এবং normalCallফিরে আসা .normalFunc()যখন XYZকল করার কোনও ডাইরেক্টেড ধরণের অন্যান্য বিশেষত্ব রয়েছে .alternativeFunc()। এটি সি # তে কীভাবে হবে?

উত্তর:


86

সি # তে, বিশেষীকরণের সবচেয়ে কাছাকাছি হ'ল আরও নির্দিষ্ট ওভারলোড ব্যবহার করা; যাইহোক, এটি ভঙ্গুর এবং প্রতিটি সম্ভাব্য ব্যবহারকে কভার করে না। উদাহরণ স্বরূপ:

void Foo<T>(T value) {Console.WriteLine("General method");}
void Foo(Bar value) {Console.WriteLine("Specialized method");}

এখানে, যদি সংকলক সংকলনের ধরণগুলি জানে, তবে এটি সুনির্দিষ্টভাবে বেছে নেবে:

Bar bar = new Bar();
Foo(bar); // uses the specialized method

যাহোক....

void Test<TSomething>(TSomething value) {
    Foo(value);
}

এর জন্য Foo<T>এমনকি ব্যবহার করবে TSomething=Bar, কারণ এটি সংকলন সময়ে পুড়ে গেছে।

অন্য একটি পদ্ধতির ধরণ-পরীক্ষার ব্যবহার করা মধ্যে যাইহোক, এই সাধারণত একটি দরিদ্র ধারণা, এবং প্রস্তাবিত না - একটি জেনেরিক পদ্ধতি।

মূলত, সি # কেবল চান না যে আপনি পলিমারফিজম ব্যতীত বিশেষত্বের সাথে কাজ করুন:

class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}

এখানে Bar.Fooসর্বদা সঠিক ওভাররাইডের সমাধান হবে।


63

ধরে নেওয়া যাক আপনি টেমপ্লেট বিশেষায়নের বিষয়ে কথা বলছেন কারণ এটি সি ++ টেম্পলেটগুলির সাহায্যে করা যেতে পারে - এর মতো বৈশিষ্ট্যটি আসলে সি # তে উপলভ্য নয়। এটি কারণ সি # জেনেরিকগুলি সংকলনের সময় প্রক্রিয়াজাত হয় না এবং রানটাইমের আরও বৈশিষ্ট্য।

তবে আপনি সি # 3.0 এক্সটেনশন পদ্ধতি ব্যবহার করে অনুরূপ প্রভাব অর্জন করতে পারেন। এখানে একটি উদাহরণ রয়েছে যা দেখায় কিভাবে কেবল MyClass<int>প্রকারের জন্য কীভাবে এক্সটেনশন পদ্ধতি যুক্ত করা যায় , এটি টেম্পলেট বিশেষায়নের মতো। তবে লক্ষ করুন, আপনি পদ্ধতির ডিফল্ট বাস্তবায়ন গোপন করতে এটি ব্যবহার করতে পারবেন না, কারণ সি # সংকলক সর্বদা প্রসারিত পদ্ধতির তুলনায় মানক পদ্ধতি পছন্দ করে:

class MyClass<T> {
  public int Foo { get { return 10; } }
}
static class MyClassSpecialization {
  public static int Bar(this MyClass<int> cls) {
    return cls.Foo + 20;
  }
}

এখন আপনি এটি লিখতে পারেন:

var cls = new MyClass<int>();
cls.Bar();

আমার যদি বিশ্বাস হয় যে কোনও জেনেরিক Barএক্সটেনশন পদ্ধতি লেখার কৌশলটি করা উচিত তবে তার চেয়ে যদি বিশেষায়নের ব্যবস্থা না করা হয় তবে যে পদ্ধতিটি ব্যবহার করা হবে তার জন্য যদি আপনি ডিফল্ট কেসটি চান তবে:

  public static int Bar<T>(this MyClass<T> cls) {
    return cls.Foo + 42;
  }

ফু প্রপার্টি বনাম বার পদ্ধতি ... আসলে কোনও সাধারণ বিশেষজ্ঞের মতো বলে মনে হচ্ছে না ...
মার্ক গ্র্যাভেল

4
না, এটি আদর্শ অনুশীলন নয়, তবে এটিই কেবল সহজ কাজ যা আপনি করতে পারেন ... (
এএফআইকে

4
এটি এক্সটেনশন staticপদ্ধতিগুলি ব্যবহার না করেও দুর্দান্ত কাজ করে দেখায় - কেবল এমন পদ্ধতি যা জেনেরিক ধরণের take এটি, @ মার্কগ্রাভেল উত্তরে নির্দেশিত সমস্যাটি নির্দিষ্ট "ডেটা" টাইপের ( / ) পদ্ধতিতে টেম্প্লেট করার পরিবর্তে MyClass<T>/ এর মতো একটি আর্গের উপর ভিত্তি করে পদ্ধতিটিকে "টেম্প্লেটিং" দ্বারা পরিবেশন করা হবে বলে মনে হচ্ছে । MyClass<int>Tint
স্লিপ ডি। থম্পসন

4
অতিরিক্ত সীমাবদ্ধতা হ'ল এটি পরোক্ষ জেনেরিক কলগুলির জন্য কাজ করবে না, যেমন কোনও পদ্ধতির মধ্যে থেকে void CallAppropriateBar<T>() { (new MyClass<T>()).Bar(); }
বার্তোসকেপিপি

18

একটি মধ্যবর্তী শ্রেণি এবং একটি অভিধান যুক্ত করে, বিশেষীকরণ সম্ভব

টিতে বিশেষজ্ঞ করতে আমরা একটি জেনেরিক ইন্টারফেস তৈরি করি, যার নাম (যেমন) প্রয়োগ বলে called ইন্টারফেস প্রয়োগ করা হয় নির্দিষ্ট শ্রেণীর জন্য, পদ্ধতিটি নির্ধারণ করে সেই শ্রেণীর জন্য নির্দিষ্ট প্রয়োগ করুন। এই মধ্যবর্তী শ্রেণিটিকে বৈশিষ্ট্য শ্রেণি বলা হয়।

জেনেরিক পদ্ধতির কলটিতে সেই বৈশিষ্ট্য শ্রেণিটিকে প্যারামিটার হিসাবে নির্দিষ্ট করা যেতে পারে, যা তখন (অবশ্যই) সর্বদা সঠিক প্রয়োগ হয়।

এটি ম্যানুয়ালি নির্দিষ্ট করার পরিবর্তে, বৈশিষ্ট্য শ্রেণিটি একটি বিশ্বব্যাপীও সংরক্ষণ করা যেতে পারে IDictionary<System.Type, object>। এরপরে এটি সন্ধান করা যায় এবং ভয়েলা, আপনার সেখানে সত্যিকারের বিশেষজ্ঞ রয়েছে।

সুবিধাজনক হলে আপনি এটি কোনও এক্সটেনশন পদ্ধতিতে প্রকাশ করতে পারেন।

class MyClass<T>
{
    public string Foo() { return "MyClass"; }
}

interface BaseTraits<T>
{
    string Apply(T cls);
}

class IntTraits : BaseTraits<MyClass<int>>
{
    public string Apply(MyClass<int> cls)
    {
        return cls.Foo() + " i";
    }
}

class DoubleTraits : BaseTraits<MyClass<double>>
{
    public string Apply(MyClass<double> cls)
    {
        return cls.Foo() + " d";
    }
}

// Somewhere in a (static) class:
public static IDictionary<Type, object> register;
register = new Dictionary<Type, object>();
register[typeof(MyClass<int>)] = new IntTraits();
register[typeof(MyClass<double>)] = new DoubleTraits();

public static string Bar<T>(this T obj)
{
    BaseTraits<T> traits = register[typeof(T)] as BaseTraits<T>;
    return traits.Apply(obj);
}

var cls1 = new MyClass<int>();
var cls2 = new MyClass<double>();

string id = cls1.Bar();
string dd = cls2.Bar();

আমার সাম্প্রতিক ব্লগের এই লিঙ্কটি এবং একটি বিস্তৃত বিবরণ এবং নমুনার জন্য অনুসরণগুলি দেখুন।


এটি ফ্যাক্টরি প্যাটার্ন এবং
জেনেরিকের

4
@ ইয়ুর আমার কাছে আমার কাছে পাঠ্যপুস্তকের সজ্জা বিন্যাসের মতো দেখাচ্ছে।
স্লিপ ডি। থম্পসন

14

আমিও টেমপ্লেট বিশেষায়নের অনুকরণ করার জন্য একটি প্যাটার্নটি অনুসন্ধান করছিলাম। কিছু পদ্ধতি রয়েছে যা কিছু পরিস্থিতিতে কাজ করতে পারে। তবে মামলার কী আছে

static void Add<T>(T value1, T value2)
{
    //add the 2 numeric values
}

যেমন বিবৃতি ব্যবহার করে ক্রিয়াটি চয়ন করা সম্ভব হবে if (typeof(T) == typeof(int))। তবে একক ভার্চুয়াল ফাংশন কলের ওভারহেড সহ বাস্তব টেম্পলেট বিশেষায়নের সিমুলেট করার আরও ভাল উপায় আছে:

public interface IMath<T>
{
    T Add(T value1, T value2);
}

public class Math<T> : IMath<T>
{
    public static readonly IMath<T> P = Math.P as IMath<T> ?? new Math<T>();

    //default implementation
    T IMath<T>.Add(T value1, T value2)
    {
        throw new NotSupportedException();    
    }
}

class Math : IMath<int>, IMath<double>
{
    public static Math P = new Math();

    //specialized for int
    int IMath<int>.Add(int value1, int value2)
    {
        return value1 + value2;
    }

    //specialized for double
    double IMath<double>.Add(double value1, double value2)
    {
        return value1 + value2;
    }
}

আগাম টাইপটি না জেনে এখন আমরা লিখতে পারি:

static T Add<T>(T value1, T value2)
{
    return Math<T>.P.Add(value1, value2);
}

private static void Main(string[] args)
{
    var result1 = Add(1, 2);
    var result2 = Add(1.5, 2.5);

    return;
}

যদি বিশেষায়িতকরণটি কেবল প্রয়োগকৃত ধরণের জন্য নয়, তবে উত্পন্ন প্রকারের জন্যও ডাকা উচিত, তবে Inইন্টারফেসের জন্য একটি প্যারামিটার ব্যবহার করতে পারে । তবে এক্ষেত্রে পদ্ধতিগুলির রিটার্নের ধরণগুলি আর জেনেরিক ধরণের হতে পারে না T


এটি বেশ আশ্চর্যজনক, আপনাকে ধন্যবাদ। এটি আমাকে নির্দিষ্ট প্রকারের জন্য রচিত প্রতিটি প্রাক-বিদ্যমান পদ্ধতির গুচ্ছকে কল করার জন্য একটি জেনেরিক ইন্টারফেস তৈরি করার অনুমতি দেয়, যা (বা কমপক্ষে খুব অসুবিধা সহ) সাধারণভাবে পুনরায় লেখা যায় না। এটি দেখতে শুরু হয়েছিল যে আমাকে কিছু ভয়াবহ করতে হবে if (type == typeof(int))এবং তারপরে জেনেরিক টাইপ ডাব্লু / অতিরিক্ত বক্সিং / আনবক্সিংয়ে ফিরতে হবে return (T)(object)result;(কারণ প্রকারটি কেবল যুক্তিযুক্তভাবে পরিচিত, স্থায়ীভাবে পরিচিত নয়)
অ্যান্ড্রু রাইট

6

প্রস্তাবিত কিছু উত্তর রানটাইম টাইপ তথ্য ব্যবহার করছে: সংকলন-কাল বাউন্ড পদ্ধতি কলগুলির চেয়ে সহজাতভাবে ধীর।

সংকলক C ++ তে যেমন বিশেষত্ব প্রয়োগ করে না।

আমি সি ++ এর অনুরূপ প্রভাব অর্জনের জন্য সাধারণ সংকলকটি করার পরে কোড ইনজেক্ট করার জন্য পোস্টশার্পের দিকে নজর দেওয়ার পরামর্শ দেব।


4

আমি মনে করি ডায়নামিক রেজোলিউশন ব্যবহার করে .NET 4+ দিয়ে এটি অর্জনের একটি উপায় আছে:

static class Converter<T>
{
    public static string Convert(T data)
    {
        return Convert((dynamic)data);
    }

    private static string Convert(Int16 data) => $"Int16 {data}";
    private static string Convert(UInt16 data) => $"UInt16 {data}";
    private static string Convert(Int32 data) => $"Int32 {data}";
    private static string Convert(UInt32 data) => $"UInt32 {data}";
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Converter<Int16>.Convert(-1));
        Console.WriteLine(Converter<UInt16>.Convert(1));
        Console.WriteLine(Converter<Int32>.Convert(-1));
        Console.WriteLine(Converter<UInt32>.Convert(1));
    }
}

আউটপুট:

Int16 -1
UInt16 1
Int32 -1
UInt32 1

যা দেখায় যে বিভিন্ন ধরণের জন্য একটি পৃথক বাস্তবায়ন ডাকা হয়।


4
আমি কিছুটা কাঁদতে চাই।
ব্যবহারকারী 2864740

-1

যদি আপনি কেবল পরীক্ষা করতে চান যে কোনও ধরণের এক্সওয়াইজেড থেকে প্রাপ্ত হয়েছে, তবে আপনি এটি ব্যবহার করতে পারেন:

theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));

যদি তা হয় তবে আপনি XYZ- এ "theunmittedobject" castালতে এবং বিকল্প ফাঙ্ক () এর অনুরোধ করতে পারেন:

XYZ xyzObject = (XYZ)theunknownobject; 
xyzObject.alternativeFunc();

আশাকরি এটা সাহায্য করবে.


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

নিশ্চিত না। এটি আমার কাছে যথেষ্ট বৈধ বলে মনে হচ্ছে। যদিও প্রয়োজনের তুলনায় কিছুটা ভার্বোজ।
জলফ

4
এটি আমার ছিল না তবে কারণ প্রশ্নের উত্তরটি সম্পূর্ণ অপ্রাসঙ্গিক। চেহারা"c++ template specialization"
জর্জিওজড

এটি সবসময় কাজ করে না। উদাহরণস্বরূপ আপনি টি দেখতে পাচ্ছেন না এবং এটি একটি বোলে কাস্ট হয়েছে।
কোস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.