วิธีเพิ่มไปยังจุดของ Sweko:
สาเหตุที่ทำให้นักแสดง
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
เป็นไปไม่ได้เพราะList<T>
เป็นค่าคงที่ในประเภท 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>()
สามารถนำไปใช้กับคอลเลกชันเพื่อฉายคอลเลกชันใหม่ขององค์ประกอบที่โยนไป T อย่างไรก็ตามการทำเช่นนี้จะโยนInvalidCastException
ถ้าการโยนบนองค์ประกอบหนึ่งหรือมากกว่าเป็นไปไม่ได้ (ซึ่งจะเป็นพฤติกรรมเดียวกันกับการทำชัดเจน ร่ายในforeach
วงของ OP )
การกรองและการหล่อด้วย OfType<T>()
ถ้ารายการการป้อนข้อมูลที่มีองค์ประกอบที่แตกต่างกันชนิดเข้ากันไม่ได้ที่มีศักยภาพInvalidCastException
สามารถหลีกเลี่ยงได้โดยใช้แทน.OfType<T>()
.Cast<T>()
( .OfType<>()
ตรวจสอบเพื่อดูว่าองค์ประกอบสามารถแปลงเป็นประเภทเป้าหมายได้หรือไม่ก่อนลองแปลงและกรองประเภทที่เข้ากันไม่ได้ออก)
แต่ละ
โปรดทราบด้วยว่าหาก OP ได้เขียนสิ่งนี้แทน: (โปรดสังเกตอย่างชัดเจน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
กรองเฉพาะ Elephants - เช่น Zebras จะถูกกำจัด
Re: การใช้ตัวดำเนินการคาสต์โดยนัย
หากไม่มีตัวแปลงแบบไดนามิกตัวดำเนินการแปลงที่ผู้ใช้กำหนดจะใช้ในการคอมไพล์เวลา * เท่านั้นดังนั้นแม้ว่าตัวดำเนินการแปลงระหว่างบอกว่า Zebra และ Elephant พร้อมใช้งานพฤติกรรมของเวลาทำงานด้านบนของวิธีการแปลงจะไม่เปลี่ยนแปลง
หากเราเพิ่มตัวดำเนินการแปลงเพื่อแปลง Zebra เป็น Elephant:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
แต่ด้วยตัวดำเนินการแปลงข้างต้นคอมไพเลอร์จะสามารถเปลี่ยนประเภทของอาร์เรย์ด้านล่างจากAnimal[]
เป็นElephant[]
ได้เนื่องจาก Zebras สามารถแปลงเป็นชุดของช้างที่เป็นเนื้อเดียวกันได้แล้ว:
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