चूंकि किसी और ने स्पष्ट रूप से यह उत्तर नहीं दिया है, इसलिए मैं निम्नलिखित जोड़ूंगा:
एक संरचना पर एक इंटरफ़ेस को लागू करने का कोई नकारात्मक परिणाम नहीं है।
संरचना को धारण करने के लिए उपयोग किए जाने वाले इंटरफ़ेस प्रकार के किसी भी चर का उपयोग उस संरचना के बॉक्सिंग मूल्य में किया जाएगा। यदि संरचना अपरिवर्तनीय है (अच्छी बात है) तो यह सबसे खराब प्रदर्शन का मुद्दा है जब तक आप नहीं हैं:
- लॉकिंग उद्देश्यों के लिए परिणामी वस्तु का उपयोग करना (किसी भी तरह से बहुत बुरा विचार)
- संदर्भ समानता शब्दार्थ का उपयोग करना और एक ही संरचना से दो बॉक्सिंग मूल्यों के लिए काम करने की अपेक्षा करना।
इन दोनों की संभावना नहीं होगी, इसके बजाय आप निम्नलिखित में से एक होने की संभावना है:
जेनेरिक्स
शायद इंटरफेस को लागू करने के लिए कई उचित कारण हैं ताकि उन्हें बाधा के साथ एक सामान्य संदर्भ में इस्तेमाल किया जा सके । जब इस तरह से चर का उपयोग किया जाता है:
class Foo<T> : IEquatable<Foo<T>> where T : IEquatable<T>
{
private readonly T a;
public bool Equals(Foo<T> other)
{
return this.a.Equals(other.a);
}
}
- एक प्रकार के पैरामीटर के रूप में संरचना का उपयोग सक्षम करें
- इसलिए जब तक कोई अन्य बाधा
new()
या class
उपयोग नहीं किया जाता है।
- इस तरह से उपयोग की जाने वाली संरचनाओं पर मुक्केबाजी से बचने की अनुमति दें।
तब यह एक इंटरफ़ेस संदर्भ नहीं है, इस प्रकार इसमें जो कुछ भी रखा जाता है उसका एक बॉक्स नहीं होता है। इसके अलावा जब c # कंपाइलर जेनेरिक कक्षाओं को संकलित करता है और टाइप पैरामीटर T के इंस्टेंस पर परिभाषित आवृत्ति विधियों के इनवोकेशन सम्मिलित करने की आवश्यकता होती है, तो यह विवश ऑपकोड का उपयोग कर सकता है :
यदि यह टाइप वैल्यू टाइप है और यह टाइप एप्लाइंसेज विधि है, तो ptr को इस टाइप द्वारा मेथड के कार्यान्वयन के लिए कॉल विधि इंस्ट्रक्शन के 'इस' पॉइंटर के रूप में अनमॉडिफाइड किया जाता है।
यह बॉक्सिंग से बचा जाता है और चूंकि मूल्य प्रकार इंटरफ़ेस को लागू कर रहा है इसलिए इसे विधि को लागू करना चाहिए , इस प्रकार कोई बॉक्सिंग नहीं होगी। ऊपर के उदाहरण में Equals()
मंगलाचरण this.a पर कोई बॉक्स के साथ किया जाता है 1 ।
कम घर्षण एपीआई
अधिकांश संरचनाओं में आदिम-जैसे शब्दार्थ होने चाहिए जहां बिटवाइज़ समान मानों को समान 2 माना जाता है । रनटाइम निहितार्थ में इस तरह के व्यवहार की आपूर्ति करेगा Equals()
लेकिन यह धीमा हो सकता है। इसके अलावा, इस निहित समानता को लागू करने के रूप में उजागर नहीं किया जाता है IEquatable<T>
और इस तरह से शब्दकोशों के लिए कुंजी के रूप में आसानी से उपयोग की जा रही संरचना को रोकता है जब तक कि वे स्पष्ट रूप से खुद को लागू नहीं करते हैं। इसलिए कई सार्वजनिक संरचना प्रकारों के लिए यह घोषित करना आम है कि वे लागू करें IEquatable<T>
(जहांT
सीएलआर बीसीएल के भीतर कई मौजूदा मूल्य प्रकारों के व्यवहार के अनुरूप इस आसान और बेहतर प्रदर्शन को स्वयं हैं) ।
BCL में सभी प्राइमिटिव न्यूनतम पर लागू होते हैं:
IComparable
IConvertible
IComparable<T>
IEquatable<T>
(और IEquatable
)
कई भी लागू करते हैं IFormattable
, आगे सिस्टम परिभाषित मूल्य प्रकार जैसे डेटटाइम, टाइमस्पैन और गाइड इन में से कई या सभी को लागू करते हैं। यदि आप एक जटिल संख्या संरचना या कुछ निश्चित चौड़ाई के पाठ मानों की तरह समान रूप से 'व्यापक रूप से उपयोगी' को लागू कर रहे हैं तो इनमें से कई सामान्य इंटरफेस (सही ढंग से) को लागू करने से आपकी संरचना अधिक उपयोगी और उपयोगी हो जाएगी।
बहिष्करण
जाहिर है अगर इंटरफ़ेस दृढ़ता से उत्परिवर्तन (जैसे ICollection
) का अर्थ है तो इसे लागू करना एक बुरा विचार है क्योंकि इसका मतलब यह होगा कि आपने या तो संरचनात्मक परिवर्तनशील बना दिया था (पहले से वर्णित त्रुटियों के कारण जहां मूल के बजाय बॉक्सिंग मूल्य पर संशोधन होते हैं ) या आप Add()
अपवादों की तरह या फेंकने के तरीकों के निहितार्थों को अनदेखा करके उपयोगकर्ताओं को भ्रमित करते हैं।
कई इंटरफेस एकरूपता (जैसे IFormattable
) का मतलब नहीं है और एक निरंतर फैशन में कुछ कार्यक्षमता को उजागर करने के लिए मुहावरेदार तरीके के रूप में काम करते हैं। अक्सर संरचना का उपयोगकर्ता इस तरह के व्यवहार के लिए किसी भी बॉक्सिंग ओवरहेड की परवाह नहीं करेगा।
सारांश
जब समझदारी से किया जाता है, तो अपरिवर्तनीय मूल्य प्रकारों पर, उपयोगी इंटरफेस का कार्यान्वयन एक अच्छा विचार है
टिप्पणियाँ:
1: ध्यान दें कि कंपाइलर इसका उपयोग तब कर सकते हैं जब चर पर आभासी तरीकों का प्रयोग किया जाता है जो एक विशिष्ट संरचना प्रकार के लिए जाने जाते हैं लेकिन जिसमें वर्चुअल विधि को लागू करना आवश्यक होता है। उदाहरण के लिए:
List<int> l = new List<int>();
foreach(var x in l)
;
सूची द्वारा लौटाए गए एन्यूमरेटर एक संरचना है, सूची की गणना करते समय आवंटन से बचने के लिए एक अनुकूलन (कुछ दिलचस्प परिणामों के साथ )। हालाँकि, फॉर्च्यून के शब्दार्थ यह निर्दिष्ट करते हैं कि यदि एन्यूमरेटर लागू हो जाता है, तो पुनरावृत्ति पूरा होने के IDisposable
बाद Dispose()
उसे कॉल किया जाएगा। जाहिर है कि बॉक्सिंग कॉल के माध्यम से ऐसा होने से एन्यूमरेटर की संरचना (वास्तव में यह बदतर होगी) का कोई लाभ समाप्त हो जाएगा। इससे भी बदतर बात यह है कि अगर डिस्पोजल कॉल किसी तरह से एन्यूमरेटर की स्थिति को संशोधित करता है तो यह बॉक्सिंग उदाहरण पर होगा और जटिल मामलों में कई सूक्ष्म कीड़े पेश किए जा सकते हैं। इसलिए इस प्रकार की स्थिति में उत्सर्जित IL है:
IL_0001: newobj System.Collections.Generic.List..ctor
IL_0006: stloc.0
IL_0007: एनओपी
IL_0008: ldloc.0
IL_0009: callvirt System.Collections.Generic.List.GetEnumerator
IL_000E: stloc.2
IL_000F: br.s IL_0019
IL_0011: ldloca.s 02
IL_0013: Call System.Collections.Generic.List.get_Current
IL_0018: stloc.1
IL_0019: ldloca.s 02
IL_001B: Call System.Collections.Generic.List.MoveNext
IL_0020: stloc.3
IL_0021: ldloc.3
IL_0022: brtrue.s IL_0011
IL_0024: छोड़ दें। IL_0035
IL_0026: ldloca.s 02
IL_0028: विवश। System.Collections.Generic.List.Enumerator
IL_002E: callvirt System.IDisposable.Dispose
IL_0033: एनओपी
IL_0034: अंतिम रूप से
इस प्रकार आईडीसोपायरी के क्रियान्वयन से कोई प्रदर्शन समस्याएँ उत्पन्न नहीं होती हैं और एन्युमरेटर का (पछतावा) उत्परिवर्तित पहलू संरक्षित होता है, डिस्पोज़ विधि वास्तव में कुछ भी करना चाहिए!
2: डबल और फ्लोट इस नियम के अपवाद हैं जहां NaN मूल्यों को समान नहीं माना जाता है।