2013-10-17 14 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它是一样的。