I'm dealing with a problem which is giving me a headhache. I've been reading a few posts about the issues related with covariance and contravariance in C# but anything seems to solve this one.
public abstract class BaseImporter<TEntity> : IImporter<TEntity> where TEntity : class
{
//don't pay attention to this method
public TEntity SomeMethodWhichUsesTheGenericType();
//this is the important one
protected abstract IBaseReader<IFileData> GetReader(Stream file, params object[] args);
}
public interface IImporter<out T> where T : class
{
//don't pay attention to this method
T SomeMethodWhichUsesTheGenericType();
}
public interface IBaseReader<out T> where T : IFileData
{
T GetFileData(Stream stream);
bool CanHandleStream(Stream stream, params object[] args);
}
public interface IFileData
{
}
Look that I have created a BaseImporter abstract class which exposes a method GetReader() which returns a IBaseReader to a certain IFileData. If you look the declaration of IBaseReader it specifies the "out" keyword related with covariance concept.
In other assembly, I have declared some classes which make use of these ones.
public class JETFileData : IFileData
{
List<string> Data { get; set; }
}
public class ExcelFileReader : BaseReader<JETFileData>
{
public override bool CanHandleStream(Stream stream, params object[] args)
{
return true;
}
public override JETFileData GetFileData(Stream stream)
{
return new JetFileData() { Data = new List<string>() };
}
}
Look that JETFileData is a IFileData and the ExcelFileReader is a reader which passing a Stream returns a JETFileData
And then I have a concrete Importer derived from BaseImporter. e.g.:
public class US_ETDocumentImporter : BaseImporter<US_ETDocument>
{
public List<IBaseReader<JETFileData>> Readers { get; set; }
public US_ETDocumentImporter()
{
this.Readers = new List<IBaseReader<JETFileData>>()
{
new ExcelFileReader()
};
}
//HERE IS WHERE I GOT THE ERROR!!!
protected override IBaseReader<JETFileData> GetReader(Stream file, params object[] args)
{
//decide which reader is able to be used to process this file
return this.Readers.Single(r => r.CanHandleStream(file, args));
}
}
So, the error says:
'Importers.US_ETDocumentImporter.GetReader(System.IO.Stream, params object[])': return type must be 'Core.IBaseReader<Core.IFileData>' to match overridden member 'Core.BaseImporter<Documents.US_ETDocument>.GetReader(System.IO.Stream, params object[])'
And an image could be helpful too:
As I said, I believe that the error is related with covariance, but I am not completely sure. It seems that the compiler doesn't get that JETFileData is an IFileData I guess. What am I doing wrong?
I don't think this has anything to do with covariance, instead the problem is you're trying to change a return type in an override method.
Change BaseImporter
to have another generic type, and constrain it to IFileData
public abstract class BaseImporter<TEntity, TFileData> : IImporter<TEntity>
where TEntity : class
where TFileData: IFileData
{
//don't pay attention to this method
public TEntity SomeMethodWhichUsesTEntity()
{
return null;
}
// changed IFileData to TFileData
protected abstract IBaseReader<TFileData> GetReader(Stream file, params object[] args);
}
Then, when you override this in US_ETDocumentImporter
you get exectly what you want:
public class US_ETDocumentImporter : BaseImporter<US_ETDocument,JETFileData>
{
public List<IBaseReader<JETFileData>> Readers { get; set; }
public US_ETDocumentImporter()
{
this.Readers = new List<IBaseReader<JETFileData>>()
{
new ExcelFileReader()
};
}
//HERE IS WHERE I GOT THE ERROR!!!
protected override IBaseReader<JETFileData> GetReader(Stream file, params object[] args)
{
//decide which reader is able to be used to process this file
return this.Readers.Single(r => r.CanHandleStream(file, args));
}
}