2012-04-27 42 views
6

Tôi gặp lỗi khi cố thêm đối tượng chung vào Danh sách <>.Không thể chuyển đổi từ loại chung sang giao diện

Nó có thể liên quan đến hiệp phương sai và contravariance nhưng tôi không chắc chắn làm thế nào để làm việc xung quanh này. Tôi đã cố gắng hạn chế các loại chung của mình bằng cách sử dụng trong đó T: IRegister.

Tôi có một giao diện đại diện cho một thanh ghi và sau đó hai lớp đại diện cho một ByteRegister và một DoubleWordRegister.

public interface IRegister 
{ 
    string Name {get;set;} 
} 

public class ByteRegister : IRegister 
{ 
... 
} 

public class DoubleWordRegister : IRegister 
{ 
... 
} 

Sau đó tôi có một lớp khác đại diện cho một khối của các thanh ghi này cùng loại.

public class RegisterBlock<T> where T:IRegister 
{ 
    private IList<T> _registers; 

... constructors, properties etc 

    public void AddRegister(T register) 
    { 
     _registers.Add(register); 
    } 
} 

Và cuối cùng tôi có một lớp RegisterMap được sử dụng để xác định danh sách khối đăng ký và mỗi đăng ký trong khối.

public class RegisterMap 
{ 
    private List<RegisterBlock<IRegister>> _blocks; 

    public RegisterMap() 
    { 
     _blocks = new List<RegisterBlock<IRegister>>(); 

     RegisterBlock<ByteRegister> block1= new RegisterBlock<ByteRegister>("Block1", 0); 
     block1.AddRegister(new ByteRegister("Reg1")); 
     block1.AddRegister(new ByteRegister("Reg2")); 
     _blocks.Add(block1); 

     RegisterBlock<DoubleWordRegister> block2= new RegisterBlock<DoubleWordRegister>("Block2", 10); 
     block2.AddRegister(new DoubleWordRegister("Reg3")); 
     block2.AddRegister(new DoubleWordRegister("Reg4")); 
     block2.AddRegister(new DoubleWordRegister("Reg5")); 
     _blocks.Add(block2); 
    } 
} 

Tuy nhiên tôi nhận được lỗi sau:

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>' trên dòng _blocks.Add (block1) và tương tự trên _blocks.Add (block2);

+1

Câu hỏi của bạn là gì? Lỗi trình biên dịch khá rõ ràng. – phoog

Trả lời

5

Đây thực sự là một **variance problem. Bạn sẽ cần một giao diện cho lớp RegisterBlock của bạn, có lẽ IRegisterBlock:

public class RegisterBlock<T> : IRegisterBlock 
    where T : IRegister 

Sau đó, bạn có thể tạo một danh sách các IRegisterBlock:

private List<IRegisterBlock> _blocks; 

tôi thực sự đã có một tình huống tương tự trong mã-base của chúng tôi vào tuần trước, và đây chính là cách tôi giải quyết nó.

+0

Wow .. Tôi không bao giờ biết/chạy vào vấn đề khác biệt ** trước ngày hôm nay. Tôi thường có xu hướng KHÔNG sử dụng IEnumerable .. nhưng bây giờ tôi biết chính xác khi nào nên sử dụng nó. Cảm ơn .. Tôi nghĩ rằng tôi đã học được điều gì đó thực sự quan trọng ngày hôm nay! Phương pháp của tôi đã mong đợi một Danh sách trong đó Danh sách là điều đáng ngại, do đó, sử dụng IEnumarable có ý nghĩa hoàn hảo +1 – ppumkin

2

Chỉ các giao diện mới có thể là biến thể hoặc contravariant trong C#, vì vậy bạn không thể đánh dấu rõ ràng các biến thể RegisterBlock<> trên T theo cách bạn muốn.

Tuy nhiên, bạn không thực sự cần hiệp phương sai trong trường hợp này, bạn chỉ cần khai báo hai đối tượng bộ sưu tập của bạn như:

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister> 

Kể từ khi cả hai ByteRegisterDoubleWordRegister thực hiện IRegister bạn có thể thêm một trong hai trong số họ đến một số RegisterBlock<IRegister>

12

Tôi nhận thấy rằng bạn quên đặt câu hỏi. Bạn chỉ nói một loạt các sự kiện. Tôi sẽ giả định câu hỏi của bạn là "tại sao trình biên dịch lại tạo ra lỗi này?"

Trình biên dịch tạo ra lỗi đó vì không tạo ra lỗi đó sẽ dẫn đến sự cố khi chạy. Giả sử chúng tôi cho phép:

List<RegisterBlock<IRegister> _blocks = new List<RegisterBlock<IRegister>>(); 
RegisterBlock<ByteRegister> block1= new RegisterBlock<ByteRegister>(); 
_blocks.Add(block1); // Illegal, but suppose it was legal. 

Bây giờ điều gì sẽ dừng điều này?

RegisterBlock<IRegister> block1Again = _blocks[0]; 

Không có gì._blocks là danh sách RegisterBlock<IRegister>, do đó, tất nhiên _blocks[0] thuộc loại RegisterBlock<IRegister>. Nhưng hãy nhớ rằng tất nhiên mục đầu tiên trong danh sách thực sự là một RegisterBlock<ByteRegister>.

Bây giờ điều gì sẽ dừng điều này?

block1Again.AddRegister(new DoubleWordRegister())? 

Không có gì. block1Again thuộc loại RegisterBlock<IRegister>, có phương thức AddRegister(IRegister)DoubleWordRegister thực hiện IRegister.

Vì vậy, bạn chỉ cần đặt một thanh ghi từ kép vào một khối chỉ có thể chứa thanh ghi byte.

Rõ ràng điều đó không an toàn. Nơi duy nhất mà có thể được thực hiện bất hợp pháp tại thời gian biên dịch là trong bước đầu tiên; sự biến đổi biến đổi không hợp pháp ngay từ đầu.

Ngẫu nhiên, câu hỏi của bạn thường được hỏi nhiều lần trong ngày tại đây. Hai lần cho đến nay sáng nay:

Implementing nested generic Interfaces

0

Có lẽ nếu bạn làm điều gì tương tự.

ByteRegisterBlock : RegisterBlock<ByteRegister> 

Điều này sẽ giúp mã của bạn hoạt động, tuy nhiên, bạn sẽ mất một số tính linh hoạt.

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