2013-10-17 15 views
2

我有下面的代碼片段:如何在C#中正確地使用非泛型對象來設計泛型接口?

interface IRepositoryBase<BackupType> where BackupType : IBackup { 
    IEnumerable<BackupType> Backups { get; set; } 
    void Delete(BackupType backup); 
    BackupType GetOrCreateBackup(IFileSource source); 
    } 

    interface IRepository : IRepositoryBase<IBackup> { 
    } 

    interface IRepository<BackupType> : IRepository, IRepositoryBase<BackupType> where BackupType : IBackup { 
    } 

基本上我有是我希望能夠取代任何IRepository <BackupType>爲IRepository,萬一請求我想提出一些IRepository <BackupType>成集合(這樣我可以指定集合的​​類型)。除此之外,假設從IRepository繼承IRepository <BackupType>似乎是合理和合乎邏輯的(而不是其他方式)。我也肯定要IRepository擁有的所有屬性和IRepository < BackupType的方法>(唯一的區別在於非通用VS通用)

不幸的是,編譯器爲與代碼以下錯誤:

IRepository<BackupType> cannot implement both IRepositoryBase<IBackup> and IRepositoryBase<BackupType> because they may unify for some type parameter substitutions 

interface IRepository { 
    IEnumerable<IBackup> Backups { get; set; } 
    void Delete(IBackup backup); 
    IBackup GetOrCreateBackup(IFileSource source); 
    } 

    interface IRepository<BackupType> : IRepository where BackupType : IBackup { 
    new IEnumerable<BackupType> Backups { get; set; } 
    void Delete(BackupType backup); 
    new BackupType GetOrCreateBackup(IFileSource source); 
    } 

現在我H:

所以我與其他一些代碼從而消除這個錯誤(IRepositoryBase < >界面消失)再試一次把這些醜陋的「新」放在那裏。此外,在實施IRepository <BackupType>後,編譯器似乎仍希望實現「備份」屬性,儘管我用「新」隱藏了第一個屬性。

有人能告訴我如何正確地做到這一點? :)

+0

你應該用'T'開始你的泛型類型,例如'TBackup'(或者,因爲你的界面只有一種類型,只需'T'),而不是'BackupType',以遵循.NET通用類型命名標準。 –

+0

您應該注意,像'List ''用'Add(object)'方法擴展'IList',您可能無法始終滿足'IRepository'接口的更一般參數。如果它傳遞了錯誤的值,它可能必須拋出一個異常。如果你使用一組'IRepository'的唯一方法是將它們取出來,你可以製作一個[covariant](http://msdn.microsoft.com/en-us/library/dd799517。 (''out')接口('Backups'和'GetOrCreateBackup')和一個反向('in')接口(''Delete')。 –

回答

0

這是你想要實現的嗎?

internal class BackupType : IBackup { } 

    internal interface IFileSource { } 

    internal interface IBackup { } 

    interface IRepository<TBackup> where TBackup : IBackup 
    { 
     IEnumerable<TBackup> Backups { get; set; } 
     void Delete(TBackup backup); 
     TBackup GetOrCreateBackup(IFileSource source); 
    } 

    interface IRepository : IRepository<IBackup> { } 
    interface IRepositoryBackupType : IRepository<BackupType> { } 

    class RepositoryBackupType:IRepository,IRepositoryBackupType 
    { 
    ... (implementation of both interfaces) 
    } 

但如果u將其同時實現了接口的類,你必須實現他們明確,因爲我不相信類型的系統將能夠推斷的轉換底層集合。然而

列表<IBACKUP>可以容納所有IBACKUP和BackupType,在這種情況下,你不能只是簡單的將它轉換爲IEnumerable的<BackupType>(如沒有在名單也許BackupType2類型不能轉換爲BackupType) 名單<備份類型>可以安全地轉換爲IEnumerable <IBackup>,但是您不能僅將IBackup添加到它(出於與上述相同的原因)。

3

如果你看看微軟如何實現List<T>你會發現他們沒有繼承接口。您可以將List<T>同時作爲IListIList<T>的唯一原因是因爲List<T>實現了它們。

你可能想要的是實現非通用接口明確。這裏是一個隱性和顯性的實現之間的區別:

// Implicit 
public IEnumerable<TapeBackup> Types { get; set; } 

// Explicit 
IEnumerable<IBackup> IRepository.Types { get; set; } 

顯式接口實現隱藏在程序集元數據,交通不便,除非你先投的實例相應的接口類型。你只需要你的顯式實現包裝並調用隱式實現,因爲類型最有可能兼容。

但我不認爲你可以實現你的目標只實現一個成員一次兩個接​​口。

+0

只是一個小珍聞。我不一定會說「顯式接口實現隱藏在intellisense中」,因爲它對於一個沒有鍵入_explicitly_的對象來說簡直是不可接受的。對我而言,「隱藏在intellisense」意味着你可以輸入它,它仍然可以工作(對於一些特殊的智能感知來說,它確實存在)。對於明確的接口實現而言,情況並非如此:您_must_明確地將您的對象引用作爲接口來訪問該成員。 –

+0

對,我的意思是隱藏的部分是在Go To Definition視圖中。我不確定你稱之爲元數據?對象瀏覽器? –

+0

不知道我是否關注。顯式實現的接口成員確實存在(實際上,IIRC,顯式實現的成員在編譯時獲取生成的名稱)。程序集元數據有一個特殊的接口實現列表,它將接口成員實際映射到類實現成員。 (即「當你想訪問IRepository .Types'時,在這裏使用'MyClass.Types'當你想訪問'IRepository.Types'時,在這裏使用'MyClass.SomeGeneratedNameMember')什麼樣的Visual Studio顯示你的程序集元數據實際上只是整個數據集的一個_selective subset_ –

0

如果你有去:

interface IRepositoryBase<TBackup> where TBackup : IBackup 
    { 
    IEnumerable<TBackup> Backups { get; set; } 
    void Delete(TBackup backup); 
    TBackup GetOrCreateBackup(IFileSource source); 
    } 

    interface IRepository 
    { 
    IEnumerable<IBackup> Backups { get; set; } 
    void Delete(IBackup backup); 
    IBackup GetOrCreateBackup(IFileSource source); 
    } 

    interface IRepository<TBackup> : IRepositoryBase<TBackup>, IRepository where TBackup : IBackup 
    { 
    } 

編譯器不會抱怨。

您仍然需要實現六個成員而不是每次三個,其中一些通過顯式接口實現。我沒有解決這個問題。

當你實現genereic IEnumerable<>和必須提供兩個GetEnumerator方法,因爲你也繼承了非通用IEnumerable它是一樣的。