2011-02-07 32 views
7
public class ConfigControlBase<T> : UserControl 
    where T : ProviderBase 
{ 
    public T Provider { get; set; } 

    public void Init(T provider) 
    { 
     this.Provider = provider; 
    } 
} 


public abstract class ProviderBase 
{ 
    public abstract ConfigControlBase<ProviderBase> GetControl(); 
} 

public class ProviderXConfigControl : ConfigControlBase<ProviderX> 
{ 
} 

public class ProviderX : ProviderBase 
{ 
    public override ConfigControlBase<ProviderBase> GetControl() 
    { 
     var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>; 
     return confControl; 
    } 
} 

return confControl; ném một ngoại lệ:Đúc một loại nguyên tố chung xuống

thể không mặc nhiên chuyển đổi loại ConfigControlBase<ProviderX> để ConfigControlBase<ProviderBase>

Trả lời

21

Hãy thay đổi tên của lớp và các thuộc tính của bạn, nhưng giữ cho hình dạng giống nhau:

public class Cage<T> where T : Animal 
{ 
    public T Contents { get; set; } 
} 

public class Aquarium : Cage<Fish> { } 

public abstract class Animal 
{ 
    public abstract Cage<Animal> GetCage(); 
} 

public class Fish : Animal 
{ 
    public override Cage<Animal> GetCage() 
    { 
     return (Cage<Animal>)(new Aquarium()); 
    } 
} 

Bây giờ là rõ ràng tại sao điều này không hợp pháp? Giả sử nó là hợp pháp. Sau đó, bạn có thể làm điều này:

Fish fish = new Fish(); 
Cage<Animal> cage = fish.GetCage(); 
cage.contents = new Tiger(); 

Và bây giờ bạn có một con hổ trong bể cá của mình. Và không ai muốn điều đó.

Trình biên dịch (hoặc thời gian chạy) phải ngăn chặn lỗi kiểu này bằng cách nào đó; nó chọn để ngăn chặn nó càng sớm càng tốt. Đầu tiên nó có thể làm như vậy là trên các loại thử nghiệm cho việc chuyển đổi từ Aquarium để Cage<Animal>. Trình biên dịch biết rằng điều này cuối cùng có thể dẫn đến hổ trong bể cá, do đó, nó không cho phép chuyển đổi ở tất cả. Nếu bạn buộc trình biên dịch cho phép nó thông qua các phôi thì nó không thành công khi chạy.

+8

Ẩn dụ tuyệt vời. Tôi muốn MSDN là như thế. –

1

Điều này là do ConfigControlBase<ProviderX> không phải là một ConfigControlBase<ProviderBase>

0

của bạn
public override ConfigControlBase<ProviderBase> GetControl() 

không phù hợp

var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>; 
8

loại Generic với đối số kiểu chuyển nhượng không được chuyển nhượng tự. Ví dụ:
Ví dụ: bạn không thể truyền List<string> đến List<object>, mặc dù stringobject.

Nó không phải là ngay lập tức rõ ràng lý do tại sao đúc như không được hỗ trợ vì vậy hãy để tôi cho bạn một ví dụ:

var words = new List<string> { "Serve God", "love me", "mend" }; 
var objects = (List<object>) words; // C# compiler wouldn't allow this 
objects.Add (new Car()); // we just added a Car to Shakespeare's work and the universe exploded 

C# không khuyến khích vũ trụ nổ, tuy nhiên kể từ C# 4.0 phiên bản ánh sáng của ý tưởng này được thực hiện . Bạn thấy, trong một số trường hợp việc đúc như vậy thực sự sẽ an toàn.

.NET 4.0 mang đến khái niệm hiệp phương sai và đối nghịch trong hình tướng chỉ dành cho giao diện và đại biểu, bạn có thể muốn kiểm tra điều này.

Ví dụ (không hoạt động trước khi .NET 4.0):

void HandleCollection (IEnumerable<object> collection) 
{ 
    // ... 
} 

var words = new List<string> { "Serve God", "love me", "mend" }; 

// IEnumerable is defined as IEnumerable<out T> in .NET 4.0 
// 'out' keyword guarantees that T is only used for return values 
// and therefore client code can't explode the universe 

var objects = (IEnumerable<object>) words; 
HandleCollection (objects); 
+4

"C# không khuyến khích nổ vũ trụ" là phương châm mới của tôi. –

+1

Lưu ý rằng * hiệp phương sai không hoạt động trên các loại giá trị *. Bạn không thể chuyển đổi một danh sách int thành một IEnumerable vì bộ nhớ phải được cấp phát cho quyền anh, và không có mã nào được phát ra để làm điều đó. Bạn có thể chuyển đổi một danh sách chuỗi thành IEnumerable , bởi vì chuỗi là một kiểu tham chiếu. –

+0

Tôi đã thay đổi các ví dụ thành 'chuỗi' để giữ tính nhất quán. Cảm ơn nhận xét. –

0

Câu trả lời này có thể không hữu ích trong kịch bản của bạn, vì có lẽ bạn nên tìm giải pháp khác, nhưng trong khi phản ánh tôi thấy khả năng truyền sang loại ít chung hơn rất hữu ích, do đó tôi đã viết giải pháp cho nó. Nó chỉ hoạt động cho các giao diện tuy nhiên, và bạn phải đảm bảo rằng bạn sẽ chỉ truyền các đối tượng của các kiểu chính xác đến giao diện.

Tôi về cơ bản tạo một lớp proxy tại thời gian chạy thực hiện tất cả các phôi bắt buộc cho bạn. Cách sử dụng của nó trông như sau:

object validator; // An object known to implement IValidation<T>. 
object toValidate; // The object which can be validated by using the validator. 

// Assume validator is IValidation<string> and toValidate a string. 

IValidation<object> validation 
    = Proxy.CreateGenericInterfaceWrapper<IValidation<object>>(validator); 

validation.IsValid(toValidate); // This works! No need to know about the type. 

// The following will throw an InvalidCastException. 
//validation.IsValid(10); 

Thông tin khác và mã nguồn có thể tìm thấy on my blog.

Các vấn đề liên quan