2010-04-11 7 views
20

मैं कुछ सामान्य वर्गों के लिए बहुत से विस्तार विधियां बनाना चाहता हूं, उदा।सामान्य स्थैतिक वर्ग में विस्तार विधियों को घोषित करना असंभव क्यों है?

public class SimpleLinkedList<T> where T:IComparable 

और के लिए मैं इस तरह के तरीकों बनाने शुरू कर दिया है:

public static class LinkedListExtensions 
{ 
    public static T[] ToArray<T>(this SimpleLinkedList<T> simpleLinkedList) where T:IComparable 
    { 
     //// code 
    } 
} 

लेकिन जब मैं इस तरह सामान्य LinkedListExtensions वर्ग करने की कोशिश की:

public static class LinkedListExtensions<T> where T:IComparable 
{ 
    public static T[] ToArray(this SimpleLinkedList<T> simpleLinkedList) 
    { 
     ////code 
    } 
} 

मैं "विस्तार तरीकों से कर सकते हैं केवल गैर-जेनेरिक, गैर-नेस्टेड स्थैतिक वर्ग में घोषित किया जाए "।

और मैं यह अनुमान लगाने की कोशिश कर रहा हूं कि यह प्रतिबंध कहां से आया था और कोई विचार नहीं है।

संपादित करें: अभी भी समस्या का स्पष्ट दृष्टिकोण नहीं है। ऐसा लगता है कि यह किसी कारण से लागू नहीं किया गया था।

+0

@ डैन ब्रायंट, स्थैतिक जेनेरिक कक्षाओं की अनुमति है। केवल विस्तार विधियों वाले वर्गों के लिए नहीं। – Dykam

+0

प्रश्न के अलावा ... आपके पहले उदाहरण में क्यों है। ToArray() को एक्सटेंशन विधि के रूप में परिभाषित किया जा रहा है? यदि यह क्लास SimpleLinkedList क्लास में है, तो निश्चित रूप से आप केवल एक सार्वजनिक टी [] ToArray() विधि को परिभाषित कर सकते हैं। –

+0

शायद कंपेलरों के लेखन को सरल बनाने के लिए (केवल सी # कंपाइलर नहीं) क्योंकि यह जांचने के लिए शर्तों की संख्या को कम करता है। –

उत्तर

0

बस एक विचार है, लेकिन क्या कोई कारण है कि आप इस वर्ग से न केवल प्राप्त कर सकते हैं और विस्तार विधियों के सूट को लिखने के बजाय विशेषज्ञता के लिए अतिरिक्त तरीकों को जोड़ सकते हैं? मुझे यकीन है कि आपके पास आपके कारण हैं लेकिन बस वहां फेंक रहे हैं।

1

एक बहुत ही रोचक सवाल है, मैंने स्थिर जेनेरिक वर्गों का उपयोग करने के लिए कभी भी परीक्षा नहीं दी है, लेकिन कम से कम ऐसा लगता है।

विस्तार विधियों की घोषणा के संदर्भ में, आप केवल एक सामान्य जेनेरिक प्रकार (जैसे IEnumerable<T>) के लिए एक्सटेंशन विधियों की घोषणा नहीं कर सकते हैं बल्कि समीकरण में टाइप पैरामीटर T भी ला सकते हैं। अगर हम विभिन्न प्रकार के रूप में IEnumerable<int> और IEnumerable<string> का इलाज करने के लिए सहमत हैं, तो यह एक वैचारिक स्तर पर भी समझ में आता है।

एक स्थैतिक जेनेरिक कक्षा में अपनी विस्तार विधियों की घोषणा करने में सक्षम होने से आप IEnumerable<T> where T : IComparable के लिए सभी एक्सटेंशन विधियों को समूहित करने के बाद, बार-बार अपनी प्रकार की पैरामीटर बाधाओं को दोहरा सकते हैं।

विनिर्देश के अनुसार (उद्धरण वांछित), विस्तार विधियों को केवल स्थैतिक नॉनस्टेड और गैर-सामान्य वर्गों में घोषित किया जा सकता है। पहली दो बाधाओं का कारण काफी स्पष्ट है:

  1. किसी भी राज्य को नहीं ले सकता क्योंकि यह मिश्रण नहीं है, केवल सिंटेक्टिक चीनी है।
  2. एक ही व्याख्यात्मक दायरे के रूप में होना चाहिए क्योंकि यह एक्सटेंशन प्रदान करता है।

गैर-सामान्य स्थैतिक वर्गों पर प्रतिबंध मेरे लिए थोड़ा मनमाना लगता है, मैं यहां तकनीकी कारण नहीं आ सकता। लेकिन हो सकता है कि भाषा डिजाइनरों ने आपको लिखित विस्तार विधियों में हतोत्साहित करने का निर्णय लिया जो कि एक सामान्य वर्ग के प्रकार पैरामीटर पर निर्भर करता है जिसके लिए आप विस्तार विधियां प्रदान करना चाहते हैं। इसके बजाय वे चाहते हैं कि आप अपनी विस्तार विधि का वास्तव में सामान्य कार्यान्वयन प्रदान करें, लेकिन अपने सामान्य कार्यान्वयन के अतिरिक्त अपने विस्तार विधियों के अनुकूलित/विशिष्ट (संकलित-समयबद्ध) संस्करण प्रदान करना संभव बनाएं।

मुझे C++ में टेम्पलेट विशेषज्ञता के बारे में याद दिलाता है। संपादित करें: दुर्भाग्यवश यह गलत है, कृपया नीचे मेरे जोड़ देखें।


ठीक है, क्योंकि यह वास्तव में एक दिलचस्प विषय है क्योंकि मैंने कुछ और शोध किया था।वास्तव में एक तकनीकी प्रतिबंध है कि मैं यहां चूक गया।

public static class Test 
{ 
    public static void DoSomething<T>(this IEnumerable<T> source) 
    { 
     Console.WriteLine("general"); 
    } 
    public static void DoSomething<T>(this IEnumerable<T> source) where T :IMyInterface 
    { 
     Console.WriteLine("specific"); 
    } 
} 

यह वास्तव में इस संकलक त्रुटि के साथ विफल हो जाएगा: चलो कुछ कोड पर नजर डालते हैं

Type 'ConsoleApplication1.Test' already defines a member called 'DoSomething' with the same parameter types

ठीक है, अगले हम इसे विभाजित करने का प्रयास दो अलग अलग एक्सटेंशन वर्गों में:

public interface IMyInterface { } 
public class SomeType : IMyInterface {} 

public static class TestSpecific 
{ 
    public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface 
    { 
     Console.WriteLine("specific"); 
    } 
} 
public static class TestGeneral 
{ 
    public static void DoSomething<T>(this IEnumerable<T> source) 
    { 
     Console.WriteLine("general"); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var general = new List<int>(); 
     var specific = new List<SomeType>(); 

     general.DoSomething(); 
     specific.DoSomething(); 

     Console.ReadLine(); 
    } 
} 

मेरी शुरुआती छाप के खिलाफ (यह कल रात देर हो चुकी थी), इसके परिणामस्वरूप कॉल साइटों पर अस्पष्टता होगी। इस अस्पष्टता को हल करने के लिए किसी को पारंपरिक तरीके से विस्तार विधि को कॉल करने की आवश्यकता होगी, लेकिन यह हमारे इरादे के खिलाफ है।

तो यह हमें ऐसी स्थिति के साथ छोड़ देता है जहां विस्तार विधियों के संकलन-समयबद्ध जेनेरिक विशेषज्ञता घोषित करना संभव नहीं है। दूसरी तरफ, अभी भी कोई कारण नहीं है कि हम एक विशेष जेनेरिक प्रकार पैरामीटर के लिए विस्तार विधियों केवल घोषित नहीं कर सके। इसलिए उन्हें स्थिर जेनेरिक वर्ग में घोषित करना अच्छा लगेगा।

दूसरी ओर की तरह एक विस्तार विधि लेखन पर:

public static void DoSomething<T>(this IEnumerable<T> source) where T : IMyInterface {} 

या

public static void DoSomething(this IEnumerable<IMyInterface> source) {} 

सब भी अलग नहीं है और केवल विस्तार पर कॉल साइट बनाम पर एक छोटे से कास्टिंग की आवश्यकता है विधि पक्ष (आप शायद कुछ अनुकूलन लागू करेंगे जो विशिष्ट प्रकार पर निर्भर करता है, इसलिए आपको टी को IMyInterface या किसी भी तरह से डालना होगा)। इसलिए एकमात्र कारण यह है कि मैं फिर से आ सकता हूं, भाषा डिजाइनर केवल सामान्य रूप से सामान्य फैशन में जेनेरिक एक्सटेंशन लिखने में आपको प्रोत्साहित करना चाहते हैं।

कुछ दिलचस्प चीजें यहां हो सकती हैं यदि हम समीकरण में सह/contravariance लेते हैं, जो सी # 4.0 के साथ पेश किए जाने वाले हैं।

3

समस्या यह है कि संकलक एक्सटेंशन रिज़ॉल्यूशन कैसे करता है?

public static class LinkedListExtensions { 
    public static T[] ToArray<T>(this SimpleLinkedList<T> simpleLinkedList) where T:IComparable { 
     //// code 
    } 
} 
public static class LinkedListExtensions<T> where T:IComparable { 
    public static T[] ToArray(this SimpleLinkedList<T> simpleLinkedList) { 
     ////code 
    } 
} 

जो विधि निम्नलिखित मामले में इस्तेमाल किया जाता है:

आप दोनों विधियों का वर्णन आप को परिभाषित कहो?

SimpleLinkedList<int> data = new SimpleLinkedList<int>(); 
int[] dataArray = data.ToArray(); 

मेरा अनुमान है कि भाषा डिजाइनरों ने इस परिदृश्य से बचने के लिए गैर-सामान्य प्रकारों में विस्तार विधियों को प्रतिबंधित करने का निर्णय लिया है।

+0

जहां तक ​​मेरा परीक्षण दिखाता है कि संकलक इसे हल कर सकता है! इसका स्थैतिक वर्ग सामान्य होने के साथ कुछ लेना देना नहीं है। –

+0

@ जोहान्स रुडॉल्फ: और यह किस विधि का चयन करेगा? यह बिल्कुल मेरे लिए स्पष्ट नहीं है। – Gorpik

+0

@ जोहान्स रुडॉल्फ - आपने क्या परीक्षण किया? मूल सवाल यह था कि उदाहरण में दूसरी कक्षा संकलित नहीं होती है, तो आपने इसे कैसे संकलित किया? –

13

सामान्य शब्दों में, जब आप एक विस्तार विधि का उपयोग करें जब से तुम वर्ग निर्दिष्ट नहीं करते हैं, संकलक कोई रास्ता नहीं पता है की कौन सी क्लास जहां विस्तार विधि परिभाषित किया गया है है होगा:

static class GenStatic<T> 
{ 
    static void ExtMeth(this Class c) {/*...*/} 
} 

Class c = new Class(); 
c.ExtMeth(); // Equivalent to GenStatic<T>.ExtMeth(c); what is T? 

विस्तार के तरीकों के बाद से खुद को सामान्य किया जा सकता है, इस पर कोई वास्तविक समस्या है:

static class NonGenStatic 
{ 
    static void GenExtMeth<T>(this Class c) {/*...*/} 
} 

Class c = newClass(); 
c.ExtMeth<Class2>(); // Equivalent to NonGenStatic.ExtMeth<Class2>(c); OK 

आप आसानी से अपने उदाहरण पुनर्लेखन कर सकते हैं ताकि स्थिर वर्ग सामान्य नहीं है, लेकिन सामान्य तरीके हैं। वास्तव में, इस प्रकार .NET कक्षाएं जैसे Enumerable लिखी गई हैं।

public static class LinkedListExtensions 
    { 
    public static T[] ToArray<T>(this SimpleLinkedList<T> where T:IComparable simpleLinkedList) 
    { 
     // code 
    } 
    } 
+0

इस तथ्य के बारे में अच्छी बात यह है कि सामान्य स्थैतिक वर्गों पर गैर-सामान्य विस्तार विधियां सिरदर्द का कारण बनती हैं। –

+0

यदि विस्तार विधि उस वर्ग के किसी स्थिर सदस्य का उपयोग नहीं करती है जिसमें यह निहित है, इससे कोई फ़र्क नहीं पड़ता कि कौन सी कक्षा चुनी गई थी। विस्तार विधि को स्थिर वर्ग फ़ील्ड की तरह कुछ उपयोग करके आप अपना उदाहरण बढ़ा सकते हैं; क्षेत्र की पसंद स्पष्ट रूप से प्रासंगिक रूप से प्रासंगिक होगी, लेकिन संकलक के चयन के लिए कोई रास्ता नहीं होगा। – supercat

+0

@supercat: इससे कोई फर्क नहीं पड़ता। कंपाइलर किसी भी वर्ग का चयन नहीं कर सकता है: यह पता होना चाहिए कि कौन सी विशिष्ट कक्षा चुननी है, भले ही विधि कार्यान्वयन उन सभी के लिए बिल्कुल समान हो। – Gorpik

2

विस्तार विधियों के बारे में मत सोचें क्योंकि वे स्थिर वर्ग से जुड़े हुए हैं, जिसमें वे शामिल हैं। इसके बजाय, उनके बारे में एक विशिष्ट नामस्थान से बंधे हुए सोचें। इसलिए, स्थैतिक वर्ग जिसमें उन्हें परिभाषित किया गया है, केवल इन शैलियों को नामस्थान के भीतर घोषित करने के लिए उपयोग किया जाने वाला एक खोल है। और जब आप विभिन्न प्रकार के एक्सटेंशन विधियों के लिए कई कक्षाओं को बहुत अच्छी तरह से लिख सकते हैं, तो आपको अपने विस्तार विधियों को स्पष्ट रूप से समूहबद्ध करने के तरीके से कुछ भी ज्यादा नहीं करना चाहिए।

एक्सटेंशन विधियां कक्षा के किसी भी विशेषताओं को विस्तारित नहीं करती हैं जिसमें वे निहित हैं। विधि का हस्ताक्षर विस्तार विधि के बारे में सबकुछ परिभाषित करेगा। और जब आप अपनी विधि को इस तरह से कॉल कर सकते हैं, LinkedListExtensions.ToArray(...), मुझे विश्वास नहीं है कि विस्तार विधियों का इरादा था। इस प्रकार, मेरा मानना ​​है कि ढांचे के रचनाकारों ने शायद उन प्रतिबंधों को बनाया है जिन्हें आप डेवलपर्स को सूचित करने के तरीके के रूप में चलाते हैं कि एक्सटेंशन विधियां आत्मनिर्भर हैं और वे सीधे उस कक्षा से बंधे नहीं हैं जिसमें वे रहते हैं।

-1

स्टेटिक कक्षाएं सार के रूप में परिभाषित करने में सक्षम होना चाहिए। यदि वे ऐसा करने में सक्षम थे, तो आप निर्दिष्ट कर सकते हैं कि उन्हें सीधे उपयोग नहीं किया जा सकता है, लेकिन नियमित कक्षा की तरह विरासत में होना चाहिए और फिर जेनेरिक स्थैतिक कक्षा विस्तार विधियों के लिए करने योग्य होगी क्योंकि आप उन्हें सार वर्ग में परिभाषित कर सकते हैं, कहा वर्ग से उत्तराधिकारी, और सब कुछ ठीक से काम करेगा।

चूंकि यह अभी है, स्थिर वर्गों के साथ महत्वपूर्ण सीमाओं का एक टन है, यह केवल एक ऐसा है जो वास्तव में कई मामलों में मोजो के साथ गड़बड़ कर रहा है। कंपाइलर रिटर्न प्रकार का अनुमान लगाने में अक्षमता के साथ संयुक्त है और इस प्रकार आपको जेनेरिक फ़ंक्शंस को कॉल करने के लिए पूरे जेनेरिक कार्यान्वयन में प्रवेश करने की आवश्यकता होती है जिसमें विधि हस्ताक्षर में सभी जेनेरिक नहीं होते हैं, यह सामान बहुत कमजोर बनाता है और बहुत से बनाता है टाइपिंग जो वहां होने की आवश्यकता नहीं है।

(ऐसा भी मामला है जहां दूसरा जेनेरिक पहले की परिभाषा में परिभाषित किया गया है और यह इसका उपयोग नहीं करेगा, भले ही संकलन समय पर अनुमान लगाने के लिए स्पष्ट हो। यह एक बहुत परेशान है क्योंकि यह बहुत परेशान है पूरी तरह से स्पष्ट।) एमएस जेनरिक पर काम का एक टन है कि वे इस सामान को बेहतर बनाने के लिए कर सकते हैं।

संबंधित मुद्दे

 संबंधित मुद्दे