Cast List <T> ไปยัง List <Interface>


คำตอบ:


252

คุณไม่สามารถโยนมัน (การรักษาอัตลักษณ์อ้างอิง) - ที่จะไม่ปลอดภัย ตัวอย่างเช่น:

public interface IFruit {}

public class Apple : IFruit {}
public class Banana : IFruit {}

...

List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());

// Eek - it's a banana!
Apple apple = apples[0];

ตอนนี้คุณสามารถแปลง a List<Apple>เป็นIEnumerable<IFruit>ใน. NET 4 / C # 4 ได้เนื่องจากความแปรปรวนร่วม แต่ถ้าคุณต้องการList<IFruit>คุณจะต้องสร้างรายการใหม่ ตัวอย่างเช่น:

// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();

// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();

แต่นี่ไม่เหมือนกับการคัดเลือกรายการต้นฉบับ - เพราะตอนนี้มีสองรายการแยกกัน สิ่งนี้ปลอดภัย แต่คุณต้องเข้าใจว่าการเปลี่ยนแปลงที่เกิดขึ้นกับรายการหนึ่งจะไม่ปรากฏในรายการอื่น (แน่นอนว่าจะมีการปรับเปลี่ยนวัตถุที่รายการอ้างถึง)


6
เหมือน 'ยังอยู่ในตึก'!
Andras Zoltan

1
อะไรคือความแตกต่างระหว่างการทำรายการความแปรปรวนร่วมกับการทำรายการใหม่ <ผลงาน> (); จากนั้น foreach เหนือรายการเดิมและเพิ่มแต่ละรายการในรายการ IFruit? ในทาง foreach ... การอ้างอิงวัตถุจะเหมือนกันถูกต้อง? ดังนั้น ... ถ้าเป็นเช่นนั้นฉันก็ไม่ค่อยมีเหตุผลส่วนตัวที่คุณไม่สามารถแคสต์รายการทั้งหมดได้โดยตรง เหรอ?
Robert Noack

3
@RobertNoack: "การอ้างอิงวัตถุ" หมายความว่าอย่างไร การอ้างอิงออบเจ็กต์ของแต่ละองค์ประกอบเหมือนกัน แต่ไม่เหมือนกับการแคสต์การอ้างอิงรายการเอง สมมติว่าคุณสามารถส่งข้อมูลอ้างอิงเพื่อให้คุณมีการอ้างอิงประเภทเวลาคอมไพล์List<IFruit>ซึ่งจริงๆแล้วเป็นการอ้างอิงถึงไฟล์List<Apple>. สิ่งที่คุณจะคาดหวังว่าจะเกิดขึ้นถ้าคุณเพิ่มBananaการอ้างอิงถึงที่List<IFruit>?
Jon Skeet

1
โอ้. ฉันคิดว่าฉันต้องเรียนรู้วิธีการอ่าน ฉันคิดว่าคำตอบเดิมบอกว่าวัตถุในรายการจะถูกคัดลอกและสร้างขึ้นใหม่ซึ่งไม่สมเหตุสมผลสำหรับฉัน แต่ฉันพลาดส่วนที่มีเพียงรายการใหม่เท่านั้นที่สร้างขึ้นด้วยวัตถุเดียวกัน
Robert Noack

2
@ TrươngQuốcKhánh: ผมมีความคิดว่าใด ๆของรูปลักษณ์ที่รหัสของคุณชอบหรือไม่ว่าคุณจะมีusingคำสั่งสำหรับSystem.Linqหรือสิ่งที่คุณกำลังพยายามที่จะเรียกมันว่าที่ผมไม่แน่ใจว่าวิธีการที่คุณคาดหวังว่าฉันจะสามารถที่จะช่วยเหลือ ผมขอแนะนำให้คุณทำวิจัยมากขึ้นและหากคุณยังคงติดอยู่คุณถามคำถามที่มีเป็นตัวอย่างที่สามารถทำซ้ำได้น้อยที่สุด
Jon Skeet

6

Cast iterator และ. toList ():

List<IDic> casted = input.Cast<IDic>().ToList() จะทำเคล็ดลับ

เดิมทีฉันบอกว่าความแปรปรวนร่วมจะได้ผล - แต่อย่างที่จอนได้ชี้ให้เห็นอย่างถูกต้อง ไม่มันจะไม่!

และสร้างสรรค์ฉันยังโง่เขลาซ้ายปิดToList()โทร


Castส่งกลับIEnumerable<T>ไม่ได้List<T>- และไม่แปรปรวนจะไม่ให้การแปลงนี้เพราะมันจะไม่ปลอดภัย - ดูคำตอบของฉัน
Jon Skeet

จากหน้าที่คุณเชื่อมโยงไป: "เฉพาะประเภทอินเทอร์เฟซและประเภทผู้ร่วมประชุมเท่านั้นที่สามารถมีพารามิเตอร์ประเภทตัวแปรได้"
Jon Skeet

@ จอน - ฉันรู้ว่าToList()มันหายไปก่อนที่จะอ่านความคิดเห็นของคุณ แต่ใช่อย่างที่คุณเคยแสดงแน่นอนความแปรปรวนจะไม่ทำงาน! ดู๊!
Andras Zoltan

ขวา. แปรปรวนยังสามารถช่วยเหลือในขณะที่มันหมายความว่าคุณไม่จำเป็นต้องCastโทรใน .NET 4 ToListตราบใดที่คุณระบุอาร์กิวเมนต์ประเภทที่จะ
Jon Skeet

5

ฉันก็มีปัญหานี้และหลังจากที่ได้อ่านคำตอบจอน Skeet ผมปรับเปลี่ยนรหัสของฉันจากการใช้กับการใช้งานList<T> IEnumerable<T>แม้ว่าสิ่งนี้จะไม่ตอบคำถามดั้งเดิมของ OP ที่ว่าฉันList<Client>จะส่งไปได้List<IDic>อย่างไร แต่ก็หลีกเลี่ยงความจำเป็นในการทำเช่นนั้นและอาจเป็นประโยชน์สำหรับผู้อื่นที่พบปัญหานี้ แน่นอนว่านี่จะถือว่ารหัสที่ต้องใช้List<IDic>อยู่ภายใต้การควบคุมของคุณ

เช่น:

public void ProcessIDic(IEnumerable<IDic> sequence)
{
   // Implementation
}

แทน:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}

3

หากคุณสามารถใช้ LINQ คุณสามารถทำได้ ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();


0

สิ่งเดียวที่เป็นไปได้โดยการสร้างใหม่List<IDic>และถ่ายโอนองค์ประกอบทั้งหมด


มีความคิดเห็นว่าทำไมจึงลดลงเนื่องจากความหมายทั่วไปเหมือนกับคำตอบอื่น ๆ ทั้งหมด
Piotr Auguscik

ฉันเดาว่าคุณถูกโหวตให้ลงคะแนนเพราะคุณบอกว่ามันเป็นไปได้ที่จะสร้างรายการใหม่เท่านั้น แต่คนอื่น ๆ ได้โพสต์ในทางตรงกันข้าม ... ไม่ใช่การ
โหวต

พวกเขาสร้างรายการใหม่ แต่ไม่ใช่กับโอเปอเรเตอร์ใหม่ซึ่งไม่เปลี่ยนแปลงความจริงที่ว่าพวกเขาทำ
Piotr Auguscik

0

ใน. Net 3.5 คุณสามารถทำสิ่งต่อไปนี้:

List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());

ตัวสร้างสำหรับรายการในกรณีนี้ใช้ IEnumerable แม้ว่า
รายการจะแปลงเป็น IEnumerable เท่านั้น แม้ว่าmyObjอาจแปลงเป็น ISomeInterface ได้ แต่ประเภทที่ IEnumerable ไม่สามารถแปลงเป็น IEnumerable ได้


ปัญหาคือการทำสำเนารายการโดยส่วนใหญ่คุณต้องการดำเนินการในรายการเดิม
ม้วน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.