如何在 C# 中正确设计具有非泛型对应项的泛型接口

本文关键字:泛型 泛型接口 | 更新日期: 2023-09-27 18:35:42

我有以下代码片段:

 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替换为 IRepository,以防我想将一些 IRepository 放入集合中(这样我就可以指定集合的类型)。除此之外,假设 IRepository 继承自 IRepository(而不是相反)似乎是合理和合乎逻辑的。此外,我绝对希望IRepository具有IRepository(唯一的区别是非通用与通用)

不幸的是,编译器在代码中给出了以下错误:

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

所以我再试一次其他一些代码来消除此错误(IRepositoryBase<>接口消失了):

  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);
  }

现在我有这些丑陋的"新"在那里。此外,在实现 IRepository 时,编译器似乎仍然希望实现两个"备份"属性,尽管我用"new"隐藏了第一个属性。

有人可以告诉我如何正确地做到这一点吗?:)

如何在 C# 中正确设计具有非泛型对应项的泛型接口

如果你看看Microsoft是如何实现的List<T>你会发现它们没有继承接口。 您可以将List<T>视为IListIList<T>的唯一原因是List<T>同时实现了它们。

您可能想要的是显式实现非泛型接口。 下面是隐式实现和显式实现之间的区别:

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

显式接口实现隐藏在程序集元数据中,除非您首先将实例强制转换为适当的接口类型,否则无法访问。 您只需将显式实现包装并调用隐式实现,因为这些类型很可能是兼容的。

但是我认为您无法实现仅为两个接口实现一次成员的目标。

这是你想要实现的吗?

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)
    }

但是,如果您将拥有一个实现这两个接口的类,则必须显式实现它们,因为我认为类型系统无法推断底层集合的转换。

List可以保存所有IBackup和BackupType,但是在这种情况下,您不能简单地将其转换为IEnumerable(因为列表中可能有BackupType2类型无法转换为BackupType)List可以安全地转换为IEnumerable,但是您不能只将IBackup添加到其中(原因与上述相同)。

如果你选择:

  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 
  {
  }

编译器不会抱怨。

您仍然必须实现六个成员,而不是每次实现三个成员,其中一些成员通过显式接口实现。我没有解决方案。

当您实现泛型IEnumerable<>并且必须提供两种GetEnumerator方法时也是如此,因为您还继承了非泛型IEnumerable