問題は、IDataReader<IReadParamModel, IReadResultModel>
とIDataReader<ReaderParamModel, ReadResultModel>
は互換性のないタイプです。それらを適合させるためには、共/反動が必要であるが、TResult Get(TParam param);
でTParam
は反禁制であり、TResult
は共変であろう。つまり、2つのインターフェイスを現在の使用方法と互換性を持たせる方法はありません。
実装プロパティへのアクセスが不要な場合や、具体的な型を追加の汎用パラメータとして使用する場合は、インタフェースを直接使用することができます。次のコードには、共/反変的なIDataReader
インターフェイスに基づいて、異なるデザインを示す3つのセクションが含まれています。
コードはReader
部分に制限されています。これは、リーダーとライターの例が非常に似ているためです。 Test
メソッドは、異なる継承レベルで実際に利用可能な型のいくつかの違いを強調するために使用されます。
public interface IReadParamModel { }
public interface IReadResultModel { }
public class ReaderParamModel : IReadParamModel { }
public class ReadResultModel : IReadResultModel { }
public interface IDataReader<in TParam, out TResult>
where TParam : IReadParamModel
where TResult : IReadResultModel
{
TResult Get(TParam param);
}
// First variant - much interface usage
public class DataReader_1 : IDataReader<IReadParamModel, ReadResultModel>
{
public ReadResultModel Get(IReadParamModel param) { return null; }
}
public abstract class BaseReportService_1<TReader>
where TReader : IDataReader<IReadParamModel, IReadResultModel>
{
protected TReader reader;
// input is interface, reader.Get result is interface
protected virtual IReadResultModel Test(IReadParamModel param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_1 : BaseReportService_1<DataReader_1>
{
// input is interface, reader.Get result is concrete class
protected override IReadResultModel Test(IReadParamModel param)
{
var result = reader.Get(param);
return result;
}
}
// Second variant - less interface usage, more generic parameters
public class DataReader_2 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_2<TReader, TReaderParam>
where TReader : IDataReader<TReaderParam, IReadResultModel>
where TReaderParam : IReadParamModel
{
protected TReader reader;
// input is concrete class, reader.Get result is interface
protected virtual IReadResultModel Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_2 : BaseReportService_2<DataReader_2, ReaderParamModel>
{
// input is concrete class, reader.Get result is concrete class
protected override IReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
// Third variant - fully parameterized
public class DataReader_3 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_3<TReader, TReaderParam, TReadResult>
where TReader : IDataReader<TReaderParam, TReadResult>
where TReaderParam : IReadParamModel
where TReadResult : IReadResultModel
{
protected TReader reader;
// input is concrete class, reader.Get result is concrete class
protected virtual TReadResult Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_3 : BaseReportService_3<DataReader_3, ReaderParamModel, ReadResultModel>
{
// input is concrete class, reader.Get result is concrete class
protected override ReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
あなたが入力し、(第三の例のように)出力のための具体的な種類が必要な場合は、あなたが本当にReportServiceのためのリーダーのタイプを指定する必要がある場合、あなたは、チェックする必要があります。
// Fourth variant - decoupled
// the reader is not really needed for this example...
public class DataReader_4 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_4<TReaderParam, TReadResult>
where TReaderParam : IReadParamModel
where TReadResult : IReadResultModel
{
// reader is interface, can be assigned from DataReader_4 or different implementations
protected IDataReader<TReaderParam, TReadResult> reader;
// input is concrete class, reader.Get result is concrete class
protected virtual TReadResult Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_4 : BaseReportService_4<ReaderParamModel, ReadResultModel>
{
// input is concrete class, reader.Get result is concrete class
protected override ReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
タイプ引数としてどこに 'DataReader'を提供するのか分かりません。 – HimBromBeere
@HimBromBeere パブリッククラスReportService:BaseReportService –
Vlad
問題は、共変/相反の差異のためです。これを修正する最も簡単な方法は、 'public class DataReader:IDataReader'(および 'DataWriter'の場合と同様の変更)のように' DataReader'を宣言することです。 –
kha