Reputation: 825
I have following code below. I have two main interfaces IWatch
and IWatchService
. Oryginally Watch()
was in IWatchService
and there was no IWatch
but since that CollectionService
cannot use Watch()
method i decided (ISP
) to create IWatch
interface additionally.In CollectionService
i want in ctor pass either DatabaseWatchService
or RemoteFilesWatchService
therefore i put parameter type in ctor as IWatchService<IEntity> watchService
nevertheless when in DoIt()
method initialize fileWatcherServiceCsv
variable it says:
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; }
}
Upvotes: 4
Views: 3042
Reputation: 4177
Your RemoteFilesWatchService
implements interface IWatchService<IFileEntity>
, while your CollectionService
expects a IWatchService<IEntity>
. The two types are different, that's why it cannot convert.
Modify your CollectionService
to accept IWatchService<IFileEntity>
instead, or make RemoteFilesWatchService
implement IRemoteFilesWatchService<IEntity>
. Or use a non-generic interface in CollectionService
instead.
You cannot have a IWatchService<IFileEntity>
and treat it as a IWatchService<IEntity>
. Compare it to a List<T>
for example. You cannot expect to be able to do this:
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());
Upvotes: 5
Reputation: 3502
Making a slight change to take support from variance, should fix your issue as follows:
public interface IEntity
{
}
public interface IFileEntity : IEntity
{
...
}
public interface IWatchService<out TDataEntity> where TDataEntity : IEntity //note the "out" keyword here.
{
}
You can learn more about Variance in Generic Interfaces Here
Upvotes: 0
Reputation: 659956
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> ...
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:
out
, and the compiler verifies that is safeUpvotes: 9