Home > OS >  Generic interfaces which type implements another interface
Generic interfaces which type implements another interface

Time:11-28

Could somebody explain why it doesn't work.

I have two db context. And one common method with different return types and different queries.

public interface IDataFetcher<T> where T : IMarker
{
    public List<T> GetData();
}
public interface IFetchServiceOne<T> : IDataFetcher<T> where T : IMarker
{
//maybe some methods here
}
public interface IFetchServiceTwo<T> : IDataFetcher<T> where T : IMarker
{
//maybe some different methods here
}

Implementation:

public class FetchServiceOne<T> : IFetchServiceOne<T> where T : IMarker
{
    private readonly DBContext _dbContext;

    public FetchServiceOne(DBContext dbContext) => _dbContext = dbContext;

    public List<CrucialData> GetData()
    {
        var example = _dbContext.Test.ToList();
        return example;
    }
}
public class FetchServiceTwo<T> : IFetchServiceOne<T> where T : IMarker
{
    private readonly DBContext _dbContext;

    public FetchServiceTwo(DBContext dbContext) => _dbContext = dbContext;

    public List<CrucialDataTwo> GetData()
    {
        var example = _dbContext.Test2.ToList();
        return example;
    }
}
public class CrucialData: IMarker
{
//some properries
}
public class CrucialDataTwo: IMarker
{
//another properries
}

In the output I'm getting compile error:

Error (active) CS0738 'FetchService' does not implement interface member 'IDataFetcher.GetData()'. 'FetchService.GetData()' cannot implement 'IDataFetcher.GetData()' because it does not have the matching return type of 'List'.

CodePudding user response:

It looks like IFetchServiceOne and IFetchServiceTwo (and their implementations) should be "concrete" interfaces which implement closed versione of IDataFetcher. I.e.:

public interface IFetchServiceOne : IDataFetcher<CrucialData>
{
    //maybe some methods here
}

public class FetchServiceOne : IFetchServiceOne
{
    // ...

    public List<CrucialData> GetData()
    {
        // implementation ...
        return default;
    }
}

Otherwise you can't guarantee that T requested by user will be CrucialData for FetchServiceOne (i.e. FetchServiceOne<CrucialDataTwo>.GetData() ...)

Or just:

public class FetchServiceOne : IDataFetcher<CrucialData>
{
   // ...
}

Without need for introduction of the intermediate interface.

Note that for EF you can create a generic implementation via Set method if needed:

public class FetchServiceOne<T> : IDataFetcher<T> where T : IMarker
{
    private readonly DBContext _dbContext;

    public FetchServiceOne(DBContext dbContext) => _dbContext = dbContext;

    public List<T> GetData()
    {
        var example = _dbContext.Set<T>.ToList();
        return example;
    }
}

But note that this will fail at runtime if there is no matching DbSet property setup for provided T type.

CodePudding user response:

List<T> is neither co- nor contravariant. I.e., a List<A> is not assignment comapatible to a List<B>, even if A and B are.

Let GetData return a List<T> and also cast the IQueryable result to T before creating the list:

public class FetchServiceOne<T> : IFetchServiceOne<T> where T : IMarker
{
    ...

    public List<T> GetData()
    {
        return _dbContext.Test.Cast<T>().ToList();
    }
}

This compiles, but be aware that this is not type safe. If the user specifies a T that is not compatible to the DataSet, you will get a runtime error.

  • Related