2012-04-15 27 views
5

vấn đề của tôi là như sau. Tôi đã thực hiện một thiết kế mã cho một dự án nhà mà dường như không hoạt động. Có lẽ bạn có thể giúp tôi tìm ra nơi "mùi mã" xuất phát từ đó.Tuyên bố về biến kiểu chung trừu tượng

Ok chúng ta hãy bắt đầu: tôi đã xác định một số lớp học để quấn xung quanh loại khác nhau của các loại lưu trữ:

public abstract class Archive { } 
public class ZipArchive : Archive { } 
public class TarArchive : Archive { } 

Để xử lý với những tài liệu lưu trữ, tôi định nghĩa lớp Manager. trong đó trừu tượng định nghĩa các hành vi cần thiết,

public abstract class ArchiveManager<T> where T : Archive 
{ 
    public abstract void OpenArchive(T archive); 
} 

Và những người cụ thể, mà thực sự thực hiện behaiour cụ thể:

public class ZipArchiveManager : ArchiveManager<ZipArchive> 
{ 
    public override void OpenArchive(ZipArchive archive) { /* .. */ } 
} 

public class TarArchiveManager : ArchiveManager<TarArchive> 
{ 
    public override void OpenArchive(TarArchive archive) { /* .. */ } 
} 

gì xảy ra bây giờ là trong thời gian biên dịch, tôi không biết đó loại lưu trữ Tôi sẽ xử lý, vì vậy tôi đã thử các cách sau:

class Program 
{ 
    static void Main(string[] args) 
    { 
     ArchiveManager<Archive> archiveManager = null; 

     if (/*some condition*/) {    
      archiveManager = new ZipArchiveManager(); 
     } 
     else { 
      archiveManager = new TarArchiveManager(); 
     } 
    } 
} 

kết thúc bằng lỗi sau:

Cannot implicitly convert type 'ZipArchiveManager' to 'ArchiveManager'

Theo tôi hiểu, đối số chung không thể được chuyển đổi hoàn toàn. Có cách nào để giải quyết vấn đề này không? Mã/thiết kế này có "mùi" không?

Cảm ơn bạn rất nhiều trước.

+0

Tạo lớp cơ sở không chung chung hoặc sử dụng giao diện covariant. – CodesInChaos

+1

Ngoài ra, chữ ký của 'OpenArchive' có vẻ sai với tôi. Không nên nó nhận được một dòng, và trả về 'T'? – CodesInChaos

Trả lời

2

Bạn có thể sử dụng giao diện contravariant thay vì lớp trừu tượng không triển khai bất kỳ chức năng nào. Trong trường hợp này, bạn chỉ có thể sử dụng tham số loại như một giá trị trả về của một phương pháp, không phải là một cuộc tranh cãi:

public interface IArchiveManager<out T> 
    where T : Archive 
{ 
    T OpenArchive(Stream stream); 
} 

Sau đó, bạn chỉ cần thực hiện giao diện trong lớp học quản lý của bạn:

public class ZipArchiveManager : IArchiveManager<ZipArchive> 
{ 
    public ZipArchive OpenArchive(Stream stream) 
    { 
     // ... 
    } 
} 

public class TarArchiveManager : IArchiveManager<TarArchive> 
{ 
    public TarArchive OpenArchive(Stream stream) 
    { 
     // ... 
    } 
} 
+0

Cảm ơn bạn đã trả lời. Giao diện contravariant dường như là cách duy nhất để khắc phục điều này. Tuy nhiên tôi tin rằng tôi phải suy nghĩ lại toàn bộ kiến ​​trúc của mình ...: ( – Flagg1980

0

tôi tìm thấy một cách khác bằng cách sử dụng từ khóa "năng động" của C# .NET 4.0 ...

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic archiveManager = null; 

     if (/*some condition*/) {    
      archiveManager = new ZipArchiveManager(); 
     } 
     else { 
      archiveManager = new TarArchiveManager(); 
     } 
    } 
} 

trình như một say mê đối với tôi;)

+0

Nhược điểm của việc sử dụng 'dynamic' là bạn mất kiểm tra kiểu tĩnh. Nếu bạn quyết định thay đổi tên phương thức, chữ ký phương thức hoặc hàm tạo, trình biên dịch sẽ không bắt được điều này đối với các loại 'động'. – Michael

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