X से y तक सह-भिन्न सरणी रूपांतरण रन-टाइम अपवाद हो सकता है


142

मेरे पास s ( ) private readonlyकी सूची है । मैं बाद में इस सूची में s जोड़ता हूं और उन लेबल को इस प्रकार जोड़ता हूं :LinkLabelIList<LinkLabel>LinkLabelFlowLayoutPanel

foreach(var s in strings)
{
    _list.Add(new LinkLabel{Text=s});
}

flPanel.Controls.AddRange(_list.ToArray());

Resharper मुझे एक चेतावनी दिखाता है Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation:।

कृपया मुझे पता लगाने में मदद करें:

  1. इसका क्या मतलब है?
  2. यह एक उपयोगकर्ता नियंत्रण है और कई वस्तुओं द्वारा लेबलों को सेटअप करने के लिए एक्सेस नहीं किया जाएगा, इसलिए कोड को इस तरह रखने से यह प्रभावित नहीं होगा।

जवाबों:


154

इसका क्या मतलब है

Control[] controls = new LinkLabel[10]; // compile time legal
controls[0] = new TextBox(); // compile time legal, runtime exception

और अधिक सामान्य शब्दों में

string[] array = new string[10];
object[] objs = array; // legal at compile time
objs[0] = new Foo(); // again legal, with runtime exception

C # में, आपको आधार प्रकार की एक सरणी (इस मामले में, नियंत्रण की एक सरणी के रूप में) के रूप में ऑब्जेक्ट की एक सरणी (आपके मामले में, LinkLabels) को संदर्भित करने की अनुमति है। यह किसी अन्य ऑब्जेक्ट को असाइन करने के लिए कानूनी समय भी संकलित करता है जो Controlकि सरणी के लिए है। समस्या यह है कि सरणी वास्तव में नियंत्रणों की एक सरणी नहीं है। रनटाइम पर, यह अभी भी LinkLabels की एक सरणी है। जैसे, असाइनमेंट, या लिखना, एक अपवाद फेंक देगा।


मैं आपके उदाहरण के अनुसार रनटाइम / संकलन समय अंतर को समझता हूं लेकिन विशेष प्रकार से आधार प्रकार कानूनी में रूपांतरण नहीं है? इसके अलावा, मैंने सूची टाइप की है और मैं LinkLabel(विशेष प्रकार) से Control(आधार प्रकार) जा रहा हूं ।
theVillageIdiot

2
हां, लिंकलेबेल से कंट्रोल में कनवर्ट करना कानूनी है, लेकिन यहां जैसा है वैसा नहीं है। यह एक से परिवर्तित करने के बारे में चेतावनी दे रहा LinkLabel[]है Control[], जो अभी भी कानूनी है, लेकिन एक रनटाइम समस्या हो सकती है। वह सब बदल गया है जिस तरह से सरणी को संदर्भित किया जा रहा है। सरणी को ही नहीं बदला गया है। मुद्दा देखें? सरणी अभी भी व्युत्पन्न प्रकार की एक सरणी है। संदर्भ आधार प्रकार की एक सरणी के माध्यम से है। इसलिए, यह आधार प्रकार के लिए एक तत्व निर्दिष्ट करने के लिए समय कानूनी संकलन है। फिर भी रनटाइम प्रकार इसका समर्थन नहीं करेगा।
एंथनी Pegram

आपके मामले में, मुझे नहीं लगता कि यह एक समस्या है, आप बस नियंत्रण सूची में जोड़ने के लिए सरणी का उपयोग कर रहे हैं।
एंथनी पेगम

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

14

मैं एंथनी Pegram जवाब स्पष्ट करने की कोशिश करेंगे।

सामान्य प्रकार किसी प्रकार के तर्क पर सहसंयोजक होता है, जब वह उक्त प्रकार के मान लौटाता है (उदाहरण के Func<out TResult>उदाहरण TResult, IEnumerable<out T>रिटर्न के उदाहरण T)। यही है, अगर कुछ उदाहरणों की वापसी करते हैं TDerived, तो आप ऐसे उदाहरणों के साथ काम कर सकते हैं जैसे कि वे थे TBase

सामान्य प्रकार कुछ प्रकार के तर्क पर विरोधाभासी है जब यह उक्त प्रकार के मूल्यों को Action<in TArgument>स्वीकार करता है (जैसे उदाहरणों को स्वीकार करता है TArgument)। यही है, अगर कुछ की जरूरत है उदाहरणों TBase, आप के उदाहरणों में अच्छी तरह से पारित कर सकते हैं TDerived

यह काफी तार्किक लगता है कि जेनेरिक प्रकार जो दोनों को स्वीकार करते हैं और कुछ प्रकार के रिटर्न (जब तक कि यह जेनेरिक प्रकार के हस्ताक्षर में दो बार परिभाषित नहीं किया जाता है, उदाहरण के लिए CoolList<TIn, TOut>) संबंधित प्रकार के तर्क पर सहसंयोजक नहीं होते हैं और न ही कंट्रोवरिएंट। उदाहरण के लिए, List.NET 4 में परिभाषित किया गया है List<T>, List<in T>या नहीं List<out T>

कुछ संगतता कारणों के कारण Microsoft उस तर्क को अनदेखा कर सकता है और अपने मान प्रकार तर्क पर सरणियाँ बना सकता है। हो सकता है कि उन्होंने एक विश्लेषण किया और पाया कि ज्यादातर लोग केवल सरणियों का उपयोग करते हैं जैसे कि वे आसानी से पढ़ रहे थे (अर्थात, वे केवल सरणी आरंभीकरण का उपयोग करके कुछ डेटा को एक सरणी में लिखते हैं), और, इस तरह, लाभ संभावित समय से पहले होने वाले नुकसान से आगे निकल जाते हैं। त्रुटि जब कोई सरणी में लिखते समय सहसंयोजक का उपयोग करने की कोशिश करेगा। इसलिए इसकी अनुमति है लेकिन इसे प्रोत्साहित नहीं किया जाता है।

अपने मूल प्रश्न के लिए, मूल सूची से कॉपी किए गए मानों के साथ list.ToArray()एक नया बनाता है LinkLabel[], और (उचित) चेतावनी से छुटकारा पाने के लिए, आपको इसमें पास Control[]होना होगा AddRangelist.ToArray<Control>()काम करेंगे : अपने तर्क और रिटर्न के रूप में ToArray<TSource>स्वीकार करता IEnumerable<TSource>है TSource[]; List<LinkLabel>केवल पढ़ने के लिए उपकरण IEnumerable<out LinkLabel>, जो IEnumerableसहसंयोजक के लिए धन्यवाद , विधि IEnumerable<Control>को इसके तर्क के रूप में स्वीकार किया जा सकता है।


11

सबसे सीधे आगे "समाधान"

flPanel.Controls.AddRange(_list.AsEnumerable());

अब चूँकि आप covariantly बदल List<LinkLabel>रहे हैं IEnumerable<Control>इसलिए कोई अधिक चिंता की बात नहीं है क्योंकि किसी आइटम को किसी enumerable में "जोड़ना" संभव नहीं है।


10

चेतावनी इस तथ्य के कारण है कि आप सैद्धांतिक रूप से इसके संदर्भ के माध्यम Controlसे अन्य को जोड़ सकते हैं । यह एक क्रम अपवाद का कारण होगा।LinkLabelLinkLabel[]Control[]

रूपांतरण यहाँ हो रहा है क्योंकि AddRangeएक लेता है Control[]

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


5

समस्या का मूल कारण अन्य उत्तरों में सही ढंग से वर्णित है, लेकिन चेतावनी को हल करने के लिए, आप हमेशा लिख ​​सकते हैं:

_list.ForEach(lnkLbl => flPanel.Controls.Add(lnkLbl));

2

वीएस 2008 के साथ, मुझे यह चेतावनी नहीं मिल रही है। यह .NET 4.0 में नया होना चाहिए।
क्लैरिफिकेशन: सैम मैकक्रिल के अनुसार यह वॉशर है जो एक चेतावनी प्रदर्शित करता है।

C # कंपाइलर को पता नहीं है कि AddRangeइसमें पास किए गए एरे को संशोधित नहीं किया जाएगा। चूंकि AddRangeएक प्रकार का पैरामीटर है Control[], यह सिद्धांत रूप TextBoxमें सरणी को असाइन करने का प्रयास कर सकता है , जो कि सही सरणी के लिए पूरी तरह से सही होगा Control, लेकिन सरणी वास्तव में एक सरणी है LinkLabelsऔर इस तरह के असाइनमेंट को स्वीकार नहीं करेगा।

C # में सरणियों को सह-संस्करण बनाना Microsoft का एक बुरा निर्णय था। हालांकि, यह एक अच्छा विचार हो सकता है कि पहले प्रकार के आधार प्रकार के एक सरणी में सरणी प्रकार को असाइन करने में सक्षम होने के लिए, यह रनटाइम त्रुटियों को जन्म दे सकता है!


2
मुझे यह चेतावनी Resharper
Sam Mackrill

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.