C # में टेम्पलेट स्पेशलाइजेशन कैसे करें


84

आप C # में स्पेशलाइजेशन कैसे करेंगे?

मैं एक समस्या पैदा करूँगा। आपके पास एक टेम्पलेट प्रकार है, आपको पता नहीं है कि यह क्या है। लेकिन आप जानते हैं कि क्या XYZआप कॉल करना चाहते हैं .alternativeFunc()। एक शानदार तरीका किसी विशेष फ़ंक्शन या वर्ग को कॉल करना है और किसी भी व्युत्पन्न प्रकार के कॉल पर अन्य विशेषज्ञता होने पर normalCallवापस आना है । यह C # में कैसे किया जाएगा?.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, क्योंकि यह संकलन-समय पर जलाया जाता है।

एक अन्य तरीका यह है कि सामान्य पद्धति के भीतर टाइप-टेस्टिंग का उपयोग किया जाए - हालाँकि, यह आमतौर पर एक खराब विचार है, और इसकी अनुशंसा नहीं की जाती है।

मूल रूप से, C # नहीं चाहता कि आप बहुरूपता को छोड़कर विशेषज्ञता के साथ काम करें:

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

यहां Bar.Fooहमेशा सही ओवरराइड का समाधान होगा।


63

मान लें कि आप टेम्पलेट विशेषज्ञता के बारे में बात कर रहे हैं क्योंकि यह C ++ टेम्पलेट के साथ किया जा सकता है - इस तरह की सुविधा वास्तव में C # में उपलब्ध नहीं है। ऐसा इसलिए है क्योंकि संकलन के दौरान C # जेनरिक प्रक्रिया नहीं की जाती है और रनटाइम की एक विशेषता है।

हालाँकि, आप C # 3.0 एक्सटेंशन विधियों का उपयोग करके समान प्रभाव प्राप्त कर सकते हैं। यहां एक उदाहरण है जो दिखाता है कि केवल MyClass<int>प्रकार के लिए एक्सटेंशन विधि कैसे जोड़ें , जो कि टेम्पलेट विशेषज्ञता की तरह है। हालाँकि, ध्यान दें कि आप इसका उपयोग विधि के डिफ़ॉल्ट कार्यान्वयन को छिपाने के लिए नहीं कर सकते हैं, क्योंकि C # कंपाइलर हमेशा मानक विधियों को एक्सटेंशन विधियों के लिए प्राथमिकता देता है:

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;
  }

फू संपत्ति बार विधि बनाम ... वास्तव में एक विशिष्ट विशेषज्ञता की तरह प्रतीत नहीं होता ...
मार्क Gravell

2
नहीं, यह विशिष्ट कल्पना नहीं है, लेकिन यह एकमात्र आसान काम है जो आप कर सकते हैं ... (AFAIK)
टॉमस पेट्रीसेक

3
यह विस्तार के तरीकों का उपयोग किए बिना भी ठीक काम करता है- staticसामान्य तरीके लेने वाले तरीके। है यही कारण है, समस्या @MarcGravell जवाब दिखता है की तरह एक आर्ग के आधार पर "templating" विधि द्वारा धोखा दिया जा करने के लिए कहा MyClass<T>/ MyClass<int>, न कि विशिष्ट "डाटा" प्रकार के विधि templating से ( T/ int)।
स्लिप डी। थॉम्पसन

4
अतिरिक्त सीमा यह है कि यह अप्रत्यक्ष जेनेरिक कॉल के लिए काम नहीं करेगा, उदाहरण के लिए एक विधि के भीतर से void CallAppropriateBar<T>() { (new MyClass<T>()).Bar(); }
बार्टोज़केपी

18

एक मध्यवर्ती वर्ग और एक शब्दकोश को जोड़कर, विशेषज्ञता संभव है

टी पर विशेषज्ञ करने के लिए, हम एक सामान्य इंटरफ़ेस बनाते हैं, जिसमें एक विधि होती है (जैसे) लागू होती है। इंटरफ़ेस को लागू करने वाले विशिष्ट वर्गों के लिए, विधि को परिभाषित करना उस वर्ग के लिए विशिष्ट लागू करें। इस मध्यवर्ती वर्ग को गुण वर्ग कहा जाता है।

उस विशेषता वर्ग को जेनेरिक पद्धति की कॉल में एक पैरामीटर के रूप में निर्दिष्ट किया जा सकता है, जो तब (निश्चित रूप से) हमेशा सही कार्यान्वयन लेता है।

इसे मैन्युअल रूप से निर्दिष्ट करने के बजाय, वैश्विक में लक्षण वर्ग भी संग्रहीत किया जा सकता है 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();

इस लिंक को मेरे हालिया ब्लॉग और एक विस्तृत विवरण और नमूने के लिए अनुवर्ती देखें ।


इस फैक्टरी पैटर्न है और इससे निपटने के लिए एक सभ्य तरीका है कुछ जेनरिक की कमियों की
Yaur

1
@ मुझे मेरे लिए एक पाठ्यपुस्तक डेकोरेटर पैटर्न जैसा दिखता है।
स्लिप डी। थॉम्पसन

13

मैं टेम्पलेट विशेषज्ञता का अनुकरण करने के लिए एक पैटर्न भी खोज रहा था। कुछ दृष्टिकोण हैं जो कुछ परिस्थितियों में काम कर सकते हैं। हालांकि मामले का क्या

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))और फिर जेनेरिक टाइप w / extra बॉक्सिंग / अनबॉक्सिंग के लिए वापस कास्ट करना होगा return (T)(object)result;(क्योंकि टाइप केवल तार्किक रूप से जाना जाता है, स्टेटिक रूप से ज्ञात नहीं है)
एंड्रयू राइट

6

प्रस्तावित उत्तरों में से कुछ रनटाइम प्रकार की जानकारी का उपयोग कर रहे हैं: स्वाभाविक रूप से संकलन-टाइम बाउंड विधि कॉल की तुलना में धीमी।

कंपाइलर विशेषज्ञता को लागू नहीं करता है और साथ ही सी ++ में भी करता है।

मैं आमतौर पर संकलक को C ++ के समान प्रभाव प्राप्त करने के लिए कोड को इंजेक्ट करने के तरीके के लिए PostSharp को देखने की सलाह दूंगा।


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

जो दर्शाता है कि एक अलग कार्यान्वयन को विभिन्न प्रकारों के लिए कहा जाता है।


2
मैं थोड़ा रोना चाहता हूं।
user2864740

0

यदि आप केवल यह जांचना चाहते हैं कि क्या XYZ से एक प्रकार प्राप्त होता है, तो आप इसका उपयोग कर सकते हैं:

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

यदि हां, तो आप XYZ को "theunknownobject" डाल सकते हैं और वैकल्पिक फंक () का आह्वान कर सकते हैं:

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

उम्मीद है की यह मदद करेगा।


1
मैं ज्यादा C # नहीं जानता, लेकिन जिसने भी आपको वोट दिया है उसे ऐसा क्यों कहना चाहिए। मुझे कोई अंदाज़ा नहीं है कि जब आपका उत्तर गलत हो या कुछ भी गलत हो।

पक्का भी नहीं। यह मुझे काफी मान्य लगता है। हालाँकि आवश्यकता से थोड़ा अधिक क्रिया।
जल्फ

3
यह मैं नहीं था, लेकिन यह है क्योंकि इस सवाल का जवाब पूरी तरह से अप्रासंगिक है। लुकअप"c++ template specialization"
जॉर्जियो

यह हमेशा काम नहीं करता है। उदाहरण के लिए आप यह नहीं देख सकते हैं कि T एक बूल है और फिर एक बूल में डाली जाती है।
कोस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.