सूची <X> से सूची <Y> तक कास्टिंग के लिए छोटा सिंटैक्स?


237

मुझे पता है कि एक प्रकार से दूसरे प्रकार की वस्तुओं की सूची डालना संभव है (यह देखते हुए कि आपके ऑब्जेक्ट में एक सार्वजनिक स्थैतिक स्पष्ट ऑपरेटर विधि है एक कास्टिंग) एक समय पर निम्नानुसार है:

List<Y> ListOfY = new List<Y>();

foreach(X x in ListOfX)
    ListOfY.Add((Y)x);

लेकिन क्या एक बार में पूरी सूची डालना संभव नहीं है? उदाहरण के लिए,

ListOfY = (List<Y>)ListOfX;

@Oded: मैंने अभी थोड़ा स्पष्ट करने की कोशिश की है। चिंता मत करो, तुम नहीं, मैं समझता हूँ रहे हैं :)
BoltClock

1
माना जाता है कि X, Y से प्राप्त होता है, और Z, Y से व्युत्पन्न होता है, तो सोचें कि यदि आपने अपनी सूची में Z को जोड़ा तो क्या होगा <Y> जो वास्तव में एक सूची है <x>।
रिचर्ड फ्रेंड

जवाबों:


497

अगर Xवास्तव में Yआप का उपयोग करने में सक्षम होना चाहिए डाली जा सकती है

List<Y> listOfY = listOfX.Cast<Y>().ToList();

(एच / टी टिप्पणीकारों के बारे में पता करने के लिए कुछ बातें!)

  • आपको using System.Linq;यह एक्सटेंशन विधि प्राप्त करनी होगी
  • यह सूची में प्रत्येक आइटम को सम्मिलित करता है - सूची को ही नहीं। List<Y>कॉल के द्वारा एक नया बनाया जाएगा ToList()
  • यह विधि कस्टम रूपांतरण ऑपरेटरों का समर्थन नहीं करती है। (देखें http://stackoverflow.com/questions/14523530/why-does-the-linq-cast-helper-not-work-with-the-implicit-cast-operator )
  • यह विधि किसी ऑब्जेक्ट के लिए काम नहीं करती है जिसमें एक स्पष्ट ऑपरेटर विधि (रूपरेखा 4.0) है

12
एक और सोने का बिल्ला है। यह काफी उपयोगी था।
ouflak

6
उन विस्तार विधियों को पहचानने के लिए संकलक प्राप्त करने के लिए निम्नलिखित पंक्ति को शामिल करना होगा: System.Linq का उपयोग करना;
हाइपहुमन जुएल

8
यह भी ध्यान रखें कि यद्यपि यह सूची में प्रत्येक आइटम को शामिल करता है, सूची स्वयं डाली नहीं गई है; बल्कि एक नई सूची वांछित प्रकार के साथ बनाई गई है।
ह्यपहुमन

4
यह भी ध्यान रखें कि Cast<T>विधि कस्टम रूपांतरण ऑपरेटरों का समर्थन नहीं करती है। क्यों Linq कास्ट हेल्पर निहित कास्ट ऑपरेटर के साथ काम नहीं करता है
cld

यह उस ऑब्जेक्ट के लिए काम नहीं करता है जिसमें एक स्पष्ट ऑपरेटर विधि (रूपरेखा 4.0) है
एड्रियन

100

डायरेक्ट कास्ट var ListOfY = (List<Y>)ListOfXसंभव नहीं है, क्योंकि इसके लिए सह / कंट्राविरियनList<T> प्रकार की आवश्यकता होगी , और यह कि हर मामले में गारंटी नहीं दी जा सकती है। कृपया इस कास्टिंग समस्या के समाधान को देखने के लिए पढ़ें।

हालांकि इस तरह कोड लिखना सामान्य हो सकता है:

List<Animal> animals = (List<Animal>) mammalList;

क्योंकि हम गारंटी दे सकते हैं कि हर स्तनपायी जानवर होगा, यह स्पष्ट रूप से एक गलती है:

List<Mammal> mammals = (List<Mammal>) animalList;

चूंकि हर जानवर स्तनपायी नहीं है।

हालाँकि, C # 3 और इसके बाद के संस्करण का उपयोग करके, आप उपयोग कर सकते हैं

IEnumerable<Animal> animals = mammalList.Cast<Animal>();

यह कास्टिंग को आसान बनाता है। यह वाक्यात्मक रूप से आपके एक-एक जोड़ कोड के समतुल्य है, क्योंकि यह Mammalसूची में प्रत्येक को कास्ट करने के लिए एक स्पष्ट कास्ट का उपयोग करता है Animal, और यदि असफल नहीं होता है तो यह असफल हो जाएगा।

यदि आप कास्टिंग / रूपांतरण प्रक्रिया पर अधिक नियंत्रण पसंद करते हैं, तो आप वर्ग की ConvertAllविधि का List<T>उपयोग कर सकते हैं, जो वस्तुओं को परिवर्तित करने के लिए आपूर्ति की गई अभिव्यक्ति का उपयोग कर सकते हैं। इसमें यह जोड़ा गया है कि यह Listबदले में रिटर्न देता है IEnumerable, इसलिए .ToList()यह आवश्यक नहीं है।

List<object> o = new List<object>();
o.Add("one");
o.Add("two");
o.Add(3);

IEnumerable<string> s1 = o.Cast<string>(); //fails on the 3rd item
List<string> s2 = o.ConvertAll(x => x.ToString()); //succeeds

2
मेरा मानना ​​है कि अब तक ive कभी भी यह जवाब नहीं देता। ऊपर से मेरा इतना बेहतर है।
जमीअ

6
@ जैमीक I ने +1 नहीं किया क्योंकि वह "नहीं, यह संभव नहीं है" से शुरू होता है, जबकि इस सवाल का जवाब खोजने वाले कई लोगों को दफन कर रहा है। तकनीकी रूप से, उन्होंने ओपी के प्रश्न का अधिक अच्छी तरह से उत्तर दिया।
डैन बेचर

13

स्वेको के बिंदु में जोड़ने के लिए:

जिस कारण कलाकारों ने

var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y

संभव नहीं है क्योंकि है List<T>है प्रकार टी में अपरिवर्तनीय और इस तरह यह है कि क्या बात नहीं है Xसे व्युत्पन्न Y) - इसका कारण यह है List<T>के रूप में परिभाषित किया गया है:

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces

(ध्यान दें कि इस घोषणा में, Tयहाँ कोई अतिरिक्त प्रसरण संशोधक नहीं है)

हालाँकि, अगर आपके डिज़ाइन में उत्परिवर्तनीय संग्रह की आवश्यकता नहीं है, तो कई अपरिवर्तनीय संग्रहों पर एक उपहास संभव है , उदाहरण के लिए, जो इससे Giraffeप्राप्त होता है Animal:

IEnumerable<Animal> animals = giraffes;

ऐसा इसलिए है क्योंकि यह IEnumerable<T>सहसंयोजक का समर्थन करता है T- इससे यह समझ में आता है कि IEnumerableइसका मतलब है कि संग्रह को बदला नहीं जा सकता है, क्योंकि इसमें संग्रह से तत्वों को जोड़ने या हटाने के तरीकों का कोई समर्थन नहीं है। outकी घोषणा में कीवर्ड पर ध्यान दें IEnumerable<T>:

public interface IEnumerable<out T> : IEnumerable

( यहां इस कारण के बारे में और अधिक विवरण दिया गया है कि क्यों परस्पर संग्रह जैसे Listसमर्थन का समर्थन नहीं किया जा सकता है covariance, जबकि अपरिवर्तनीय पुनरावृत्तियों और संग्रह कर सकते हैं।)

के साथ कास्टिंग .Cast<T>()

जैसा कि दूसरों ने उल्लेख किया है, .Cast<T>()टी के InvalidCastExceptionलिए डाले गए तत्वों के एक नए संग्रह को प्रस्तुत करने के लिए एक संग्रह पर लागू किया जा सकता है, हालांकि ऐसा करने से एक या अधिक तत्वों पर डाली संभव नहीं होगी (जो कि स्पष्ट करने के समान व्यवहार होगा) ओपी के foreachपाश में डाली गई )।

के साथ फ़िल्टरिंग और कास्टिंग OfType<T>()

यदि इनपुट सूची में अलग-अलग, असंगत प्रकार के तत्व हैं, तो इसके बजाय का InvalidCastExceptionउपयोग करके संभावित से बचा जा सकता .OfType<T>()है .Cast<T>()। ( .OfType<>()यह देखने के लिए जाँच करता है कि रूपांतरण का प्रयास करने से पहले एक तत्व को लक्ष्य प्रकार में परिवर्तित किया जा सकता है या असंगत प्रकारों को फ़िल्टर करता है।)

प्रत्येक के लिए

यह भी ध्यान दें कि यदि ओपी ने इसके बजाय यह लिखा है: ( स्पष्टY y में नोट करें foreach)

List<Y> ListOfY = new List<Y>();

foreach(Y y in ListOfX)
{
    ListOfY.Add(y);
}

कि कास्टिंग का भी प्रयास किया जाएगा। हालांकि, अगर कोई कास्ट संभव नहीं है, तो एक InvalidCastExceptionपरिणाम होगा।

उदाहरण

उदाहरण के लिए, दी गई सरल (C # 6) श्रेणी पदानुक्रम:

public abstract class Animal
{
    public string Name { get;  }
    protected Animal(string name) { Name = name; }
}

public class Elephant :  Animal
{
    public Elephant(string name) : base(name){}
}

public class Zebra : Animal
{
    public Zebra(string name)  : base(name) { }
}

मिश्रित प्रकारों के संग्रह के साथ काम करते समय:

var mixedAnimals = new Animal[]
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach(Animal animal in mixedAnimals)
{
     // Fails for Zed - `InvalidCastException - cannot cast from Zebra to Elephant`
     castedAnimals.Add((Elephant)animal);
}

var castedAnimals = mixedAnimals.Cast<Elephant>()
    // Also fails for Zed with `InvalidCastException
    .ToList();

जहाँ तक:

var castedAnimals = mixedAnimals.OfType<Elephant>()
    .ToList();
// Ellie

केवल हाथियों को फ़िल्टर करता है - अर्थात ज़ेब्रा को समाप्त कर दिया जाता है।

पुन: अवैध डाली ऑपरेटरों

डायनामिक के बिना, उपयोगकर्ता परिभाषित रूपांतरण ऑपरेटर केवल संकलन-समय * पर उपयोग किए जाते हैं , इसलिए भले ही ज़ेबरा और एलिफेंट के बीच एक रूपांतरण ऑपरेटर उपलब्ध कराया गया हो, रूपांतरण के लिए दृष्टिकोणों के उपरोक्त समय व्यवहार में परिवर्तन नहीं होगा।

यदि हम एक ज़ेबरा को एक हाथी में बदलने के लिए एक रूपांतरण ऑपरेटर जोड़ते हैं:

public class Zebra : Animal
{
    public Zebra(string name) : base(name) { }
    public static implicit operator Elephant(Zebra z)
    {
        return new Elephant(z.Name);
    }
}

इसके बजाय, ऊपर रूपांतरण ऑपरेटर को देखते हुए संकलक सरणी के नीचे से के प्रकार को बदलने में सक्षम हो जाएगा Animal[]करने के लिए Elephant[], यह देखते हुए कि जेब्रा अब हाथियों का एक सजातीय संग्रह करने के लिए परिवर्तित किया जा सकता है:

var compilerInferredAnimals = new []
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

रन टाइम में इंप्लिकेंट कनवर्ज़न ऑपरेटर्स का उपयोग करना

* जैसा कि एरिक ने उल्लेख किया है, रूपांतरण ऑपरेटर को समय का सहारा लेकर चलाया जा सकता है dynamic:

var mixedAnimals = new Animal[] // i.e. Polymorphic collection
{
    new Zebra("Zed"),
    new Elephant("Ellie")
};

foreach (dynamic animal in mixedAnimals)
{
    castedAnimals.Add(animal);
}
// Returns Zed, Ellie

अरे, मैंने सिर्फ "foreach (टाइपिंग फ़िल्टरिंग के लिए) का उपयोग करके" उदाहरण के लिए उपयोग करने की कोशिश की: var list = new List <object> () {1, "a, 2," b ", 3," c ", 4," डी "}; foreach (सूची में int) Console.WriteLine (i); और जब मैं इसे चलाता हूं तो मुझे "निर्दिष्ट कास्ट मान्य नहीं होता है।" क्या मैं कुछ भूल रहा हूँ? मुझे नहीं लगता था कि इस तरह से काम किया गया था, यही वजह है कि मैं इसे आजमा रहा था।
ब्रेंट रिटेनहाउस

इसके अलावा, यह एक संदर्भ बनाम मूल्य प्रकार की चीज नहीं है। मैंने इसे 'थिंग' के आधार वर्ग और दो व्युत्पन्न वर्गों: 'व्यक्ति' और 'पशु' के साथ आज़माया। जब मैं इसके साथ एक ही काम करता हूं तो मुझे यह मिलता है: "टाइप करने के लिए 'एनिमल' कास्ट करने में असमर्थ 'टाइप' पर्सन।" तो यह निश्चित रूप से प्रत्येक तत्व के माध्यम से पुनरावृत्ति कर रहा है। अगर मुझे सूची में एक टाइप करना था तो यह काम करेगा। जब तक कंपाइलर ने इसे ऑप्टिमाइज़ नहीं किया तब तक फॉरच शायद बहुत धीमा होगा।
ब्रेंट रिटेनहाउस

धन्यवाद ब्रेंट - मैं वहाँ बिल्कुल बंद था। foreachफ़िल्टर नहीं करता है, लेकिन अधिक व्युत्पन्न प्रकार का उपयोग करने के रूप में पुनरावृत्ति चर एक कास्ट का प्रयास करने में कंपाइलर को जोर देगा, जो पहले तत्व पर विफल होगा जो अनुपालन नहीं करता है।
स्टुअर्टएलसी


3

यह इस प्रश्न का उत्तर नहीं है, लेकिन यह कुछ के लिए उपयोगी हो सकता है: @ @Weko ने कहा, सहसंयोजक और विरोधाभासी के लिए धन्यवाद, List<X>में डाला नहीं जा सकता है List<Y>, लेकिन इसमें List<X>डाली जा सकती है IEnumerable<Y>और यहां तक ​​कि निहित कलाकारों के साथ भी।

उदाहरण:

List<Y> ListOfY = new List<Y>();
List<X> ListOfX = (List<X>)ListOfY; // Compile error

परंतु

List<Y> ListOfY = new List<Y>();
IEnumerable<X> EnumerableOfX = ListOfY;  // No issue

बड़ा फायदा यह है कि यह मेमोरी में एक नई सूची नहीं बनाता है।


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

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