接口无法隐式转换类型

Hen*_*nry 1 c#

我下面有以下代码。我有两个主要界面IWatchIWatchService。Oryginally Watch()IWatchService也没有IWatch,但自认为CollectionService无法使用Watch()方法,我决定(ISP)创建IWatch界面additionally.In CollectionService我想在构造函数通过其中DatabaseWatchServiceRemoteFilesWatchService因此我把参数类型的构造函数的IWatchService<IEntity> watchService时候不过在DoIt()方法初始化fileWatcherServiceCsv变量,它说:

无法将类型'RemoteFilesWatchService'隐式转换为'IWatchService'。存在显式转换(您是否缺少演员表?)

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; }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 6

This question gets posted almost every day. One more time!

A box of apples is not a box of fruit. Why not?

You can put a banana into a box of fruit, but you cannot put a banana into a box of apples, so a box of apples is not a box of fruit, because the operations you can perform on them are different. Similarly, a box of fruit is not a box of apples.

You're trying to use a IWatchService (box) of IFileEntity (apples) as an IWatchService of IEntity (fruit), and that's not legal.

Now, you might notice that in C# you can use an IEnumerable<Apple> where an IEnumerable<Fruit> is expected. That works just fine because there is no way to put a banana into an IEnumerable<Fruit>. In every member of IEnumerable<T> and IEnumerator<T>, the T comes out, not in.

If you are in that situation then you can mark your interface as

interface IWatchService<out T> ... 
Run Code Online (Sandbox Code Playgroud)

And the compiler will verify that every T in the interface is used in "out" positions, and then will allow the conversion you want.

That conversion is called a generic covariant conversion and it only works when:

  • The generic type is an interface or delegate
  • The type parameter is marked out, and the compiler verifies that is safe
  • 各种类型(例如,Fruit和Apple)都是引用类型。例如,您不能进行涉及int和object的协变转换。


Jes*_*Wit 5

RemoteFilesWatchService实现了接口IWatchService<IFileEntity>,而您CollectionService期望的是IWatchService<IEntity>. 这两种类型不同,这就是它不能转换的原因。修改您的CollectionService以接受IWatchService<IFileEntity>,或RemoteFilesWatchService执行IRemoteFilesWatchService<IEntity>。或者使用非通用接口CollectionService代替。

你不能拥有 aIWatchService<IFileEntity>并将其视为 a IWatchService<IEntity>。将其与List<T>示例进行比较。你不能指望能够做到这一点:

class Animal {}
class Bird : Animal {}
class Elephant : Animal {}

var birds = new List<Bird>();

// compiler does not allow this...
List<Animal> animals = birds;

// ...because there is no point in adding elephants to a list of birds.
animals.Add(new Elephant());
Run Code Online (Sandbox Code Playgroud)