Để thêm vào quan điểm của Sweko:
Lý do tại sao các diễn viên
var listOfX = new List<X>();
ListOf<Y> ys = (List<Y>)listOfX; // Compile error: Cannot implicitly cast X to Y
là không thể là bởi vì List<T>
là bất biến trong Type T và do đó nó không quan trọng cho dù X
xuất phát từ Y
) - điều này là vì List<T>
được định nghĩa là:
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T> ... // Other interfaces
(Lưu ý rằng trong khai báo này, gõ T
ở đây không có sửa đổi phương sai bổ sung)
Tuy nhiên, nếu các bộ sưu tập có thể thay đổi không được yêu cầu trong thiết kế của bạn, thì việc phát sóng nhiều bộ sưu tập bất biến là có thể , ví dụ với điều kiện là Giraffe
xuất phát từ Animal
:
IEnumerable<Animal> animals = giraffes;
Điều này là do IEnumerable<T>
hỗ trợ hiệp phương sai trong T
- điều này có nghĩa là cho rằng IEnumerable
bộ sưu tập không thể thay đổi, vì nó không hỗ trợ các phương thức để Thêm hoặc Loại bỏ các phần tử khỏi bộ sưu tập. Lưu ý out
từ khóa trong khai báo IEnumerable<T>
:
public interface IEnumerable<out T> : IEnumerable
( Dưới đây là giải thích thêm cho lý do tại sao các bộ sưu tập có thể thay đổi như List
không thể hỗ trợ covariance
, trong khi các bộ lặp và bộ sưu tập bất biến có thể.)
Đúc với .Cast<T>()
Như những người khác đã đề cập, .Cast<T>()
có thể được áp dụng cho một bộ sưu tập để chiếu một bộ phần tử mới được đúc cho T, tuy nhiên làm như vậy sẽ ném ra InvalidCastException
nếu việc sử dụng một hoặc nhiều phần tử là không thể (đó sẽ là hành vi tương tự như làm rõ ràng đúc trong foreach
vòng lặp của OP ).
Lọc và đúc với OfType<T>()
Nếu danh sách đầu vào chứa các yếu tố thuộc các loại khác nhau, không thể kết hợp, tiềm năng InvalidCastException
có thể tránh được bằng cách sử dụng .OfType<T>()
thay vì .Cast<T>()
. ( .OfType<>()
kiểm tra xem liệu một phần tử có thể được chuyển đổi thành loại mục tiêu hay không, trước khi thử chuyển đổi và lọc ra các loại không phù hợp.)
cho mỗi
Cũng lưu ý rằng nếu OP đã viết điều này thay vào đó: (lưu ý rõ ràngY y
trong foreach
)
List<Y> ListOfY = new List<Y>();
foreach(Y y in ListOfX)
{
ListOfY.Add(y);
}
rằng việc đúc cũng sẽ được cố gắng. Tuy nhiên, nếu không có diễn viên là có thể, một InvalidCastException
kết quả sẽ.
Ví dụ
Ví dụ, được phân cấp lớp đơn giản (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) { }
}
Khi làm việc với một bộ sưu tập các loại hỗn hợp:
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();
Trong khi:
var castedAnimals = mixedAnimals.OfType<Elephant>()
.ToList();
// Ellie
chỉ lọc ra Voi - tức là Ngựa vằn bị loại.
Re: Toán tử diễn viên tiềm ẩn
Không có động, các toán tử chuyển đổi do người dùng xác định chỉ được sử dụng tại thời gian biên dịch *, vì vậy ngay cả khi toán tử chuyển đổi giữa Zebra và Voi được cung cấp, hành vi thời gian chạy trên của các cách tiếp cận chuyển đổi sẽ không thay đổi.
Nếu chúng ta thêm toán tử chuyển đổi để chuyển đổi Zebra thành Voi:
public class Zebra : Animal
{
public Zebra(string name) : base(name) { }
public static implicit operator Elephant(Zebra z)
{
return new Elephant(z.Name);
}
}
Thay vào đó, do các nhà điều hành chuyển đổi trên, trình biên dịch sẽ có thể thay đổi kiểu của các bên dưới mảng từ Animal[]
đến Elephant[]
, cho rằng các Zebras bây giờ có thể được chuyển đổi sang một bộ sưu tập đồng nhất của Voi:
var compilerInferredAnimals = new []
{
new Zebra("Zed"),
new Elephant("Ellie")
};
Sử dụng toán tử chuyển đổi tiềm ẩn trong thời gian chạy
* Như Eric đã đề cập, toán tử chuyển đổi có thể được truy cập vào thời gian chạy bằng cách sử dụng 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