c# - 接口(interface)不能隐式转换类型

标签 c#

我在下面有以下代码。我有两个主要接口(interface)IWatchIWatchService .原来Watch()IWatchService并且没有 IWatch但自那以后CollectionService不能使用 Watch()我决定 ( ISP ) 创建 IWatch 的方法接口(interface)另外。在CollectionService我想要 ctor pass DatabaseWatchServiceRemoteFilesWatchService因此,我将参数类型作为 IWatchService<IEntity> watchService 放在 ctor 中然而当在DoIt()方法初始化 fileWatcherServiceCsv它说的变量:

Cannot implicitly convert type 'RemoteFilesWatchService' to 'IWatchService'. An explicit conversion exists (are you missing a cast?)

public interface IWatch
{
     void Watch();
}

public interface IWatchService<TDataEntity> where TDataEntity : IEntity
{
     INotificationFactory NotificationFactory { get; }
     ObservableCollection<TDataEntity> MatchingEntries { get; set; }
}

public interface IDatabaseWatchService<TDataEntity> : IWatchService<TDataEntity> where TDataEntity : IDatabaseEntity
{
     IDatabaseRepository<IDbManager> DatabaseRepository { get; }
}

public interface IRemoteFilesWatchService<TDataEntity> : IWatchService<TDataEntity> where TDataEntity : IFileEntity
{
     List<string> ExistingRemoteFiles { get; set; }
     List<RemoteLocation> RemoteLocations { get; set; }      
     IWinScpOperations RemoteManager { get; set; }
     IRemoteFilesRepository<IDbManager, TDataEntity> RemoteFilesRepository { get; }
}

public class RemoteFilesWatchService : IRemoteFilesWatchService<IFileEntity>, IWatch
{
     public INotificationFactory NotificationFactory { get; }
     public ObservableCollection<IFileEntity> MatchingEntries { get; set; }
     public List<string> ExistingRemoteFiles { get; set; }
     public List<RemoteLocation> RemoteLocations { get; set; }
     public IWinScpOperations RemoteManager { get; set; }
     public IRemoteFilesRepository<IDbManager, IFileEntity> RemoteFilesRepository { get; }

    public RemoteFilesWatchService(IWinScpOperations remoteOperator,
                IRemoteFilesRepository<IDbManager, IFileEntity> remoteFilesRepository,
                INotificationFactory notificationFactory)
    {
           RemoteManager = remoteOperator;
           RemoteFilesRepository = remoteFilesRepository;  //csv, xml or other repo could be injected
           NotificationFactory = notificationFactory;
    }

    public void Watch()
    {
    }
}

public class DatabaseWatchService : IDatabaseWatchService<DatabaseQuery>, IWatch
{
      public INotificationFactory NotificationFactory { get; }
      public ObservableCollection<DatabaseQuery> MatchingEntries { get; set; }
      public IDatabaseRepository<IDbManager> DatabaseRepository { get; }

      public DatabaseWatchService(IDatabaseRepository<IDbManager> databaseRepository,
            INotificationFactory notificationFactory)
      {
            DatabaseRepository = databaseRepository;
            NotificationFactory = notificationFactory;
      }

      public void Watch()
      {
      }
}

public class CollectionService
{
       private IWatchService<IEntity> _watchService;     

       public CollectionService(IWatchService<IEntity> watchService)
       {
             _watchService = watchService;
       }
}

class Run
{
       void DoIt()
       {          
            IWatchService<IEntity> fileWatcherServiceCsv = new RemoteFilesWatchService(new WinScpOperations(),
                                                                  new RemoteCsvFilesRepository(new DbManager(ConnectionDbType.MySql)),
                                                                  new NotificationFactory());

        var coll1 = new CollectionService(fileWatcherServiceCsv);
        }
}

public interface IEntity
{
}


public interface IFileEntity : IEntity
{
    int Id { get; set; }
    string Name { get; set; }
    bool IsActive { get; set; }
    bool RemoveFromSource { get; set; }
    string DestinationFolder { get; set; }
    RemoteLocation RemoteLocation { get; set; }
}

public interface IDatabaseEntity : IEntity
{
}

public class CsvFile : IFileEntity
{
    public int ColumnHeader { get; set; }
    public int ColumnsCount { get; set; }
    public string Separator { get; set; }
    public int ValuesRowStartposition { get; set; }
    public int ColumnRowPosition { get; set; }
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public bool RemoveFromSource { get; set; }
    public string DestinationFolder { get; set; }
    public RemoteLocation RemoteLocation { get; set; }
}

public class XmlFile : IFileEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
    public bool RemoveFromSource { get; set; }
    public string DestinationFolder { get; set; }
    public RemoteLocation RemoteLocation { get; set; }
    public string SubNode { get; set; }
    public string MainNode { get; set; }
}

最佳答案

这个问题几乎每天都会被发布。再来一次!

一盒苹果不是一盒水果。为什么不呢?

你可以把一根香蕉放进一盒水果里,但你不能把一根香蕉放进一盒苹果里,所以一盒苹果不是一盒水果,因为你能对它们进行的操作是不同的。同样,一盒水果不是一盒苹果。

您正在尝试使用 IWatchService (盒)IFileEntity (苹果)作为 IWatchServiceIEntity (水果),这是不合法的。

现在,您可能会注意到在 C# 中您可以使用 IEnumerable<Apple>哪里有IEnumerable<Fruit>是期待。这很好用,因为 没有办法将香蕉放入 IEnumerable<Fruit>。在IEnumerable<T>的每个成员中和 IEnumerator<T> , T 出来,而不是进来

如果您处于那种情况,那么您可以将您的界面标记为

interface IWatchService<out T> ... 

并且编译器将验证每个T在界面中使用在“out”位置,然后将允许您想要的转换。

该转换称为通用协变转换,它仅在以下情况下有效:

  • 泛型是接口(interface)或委托(delegate)
  • 类型参数标记为out , 编译器验证它是安全的
  • 变体类型(比如 Fruit 和 Apple)都是引用类型。例如,您不能进行涉及 int 和对象的协变转换。

关于c# - 接口(interface)不能隐式转换类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55357270/

相关文章:

c# - 了解文件属性的最佳方式

c# - Unity接触点无碰撞器

c# - EF Core 3.1 流畅的 API

C# 无法将类型 'T' 隐式转换为 'object[*,*]'

c# - 对同一实体同时使用多对多和一对多

c# - DateTime ParseExact 抛出异常与 hh vs HH

c# - 如何使用 String.Replace

c# - 按反射排序比按成员排序更快?

c# - 静态、常量和只读字段的内存分配在哪里?

c# - 将字节数组传递给 T4 模板代码到另一个相同类型的数组