C# 继承中的多个泛型

标签 c#

如何解决?为什么我不能使用这个结构?

using System;

public class Program
{

    public interface IReadParamModel{ }
    public interface IReadResultModel{ }
    public interface IWriteParamModel{ }
    public interface IWriteResultModel{ }

    public interface IDataReader<TParam, TResult>  where TParam : IReadParamModel where TResult : IReadResultModel
    {
        TResult Get(TParam param);
    }

    public interface IDataWriter<TParam, TResult>  where TParam : IWriteParamModel where TResult : IWriteResultModel
    {
        TResult Write(TParam param);
    }

    public abstract class BaseReportService<TReader, TWriter> 
        where TReader : IDataReader<IReadParamModel, IReadResultModel>
        where TWriter : IDataWriter<IWriteParamModel, IWriteResultModel>
    {
            TWriter writer;
            TReader reader;
    }

    public class ReaderParamModel : IReadParamModel { }
    public class ReadResultModel : IReadResultModel { }

    public class WriteParamModel : IWriteParamModel { }
    public class WriteResultModel : IWriteResultModel { }

    public class DataReader : IDataReader<ReaderParamModel, ReadResultModel>
    {
        public ReadResultModel Get(ReaderParamModel param) { return null; }     

    }

    public class DataWriter : IDataWriter<WriteParamModel, IWriteResultModel>
    {
        public IWriteResultModel Write(WriteParamModel param){ return null; }   
    }

    public class ReportService : BaseReportService<DataReader, DataWriter>
    {

    }
}

Compilation error (line 46, col 15): The type 'Program.DataReader' cannot be used as type parameter 'TReader' in the generic type or method 'Program.BaseReportService'.
There is no implicit reference conversion from 'Program.DataReader' to 'Program.IDataReader'.

Compilation error (line 46, col 15): The type 'Program.DataWriter' cannot be used as type parameter 'TWriter' in the generic type or method 'Program.BaseReportService'.
There is no implicit reference conversion from 'Program.DataWriter' to 'Program.IDataWriter'.

最佳答案

问题是 IDataReader<IReadParamModel, IReadResultModel>IDataReader<ReaderParamModel, ReadResultModel>是不兼容的类型。为了使它们兼容,需要协变/逆变,但是对于 TResult Get(TParam param); , TParam将是逆变且 TResult将是协变的。这意味着,无法使这两个接口(interface)与其当前的用法兼容。

选择是,如果不需要访问实现属性,则直接使用接口(interface),或者使用具体类型作为附加通用参数。以下代码包含三个部分,演示了基于协变/逆变 IDataReader 的不同设计。界面。

该代码仅限于 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;
    }
}

关于C# 继承中的多个泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40841836/

相关文章:

c# - 警报在 Chrome 驱动程序中不起作用

c# - 如何设置 LUISDialog 转到 .NET 中的“无意图”的最小阈值?

c# - WCF:读取 XML 数据时已超出最大数组长度配额 (16384)

c# - 使用 EVOPdf 版本 6.10 将 Html 转换为 pdf

c# - System.Drawing.Graphics,绘制表格?

c# - awesomium 中的网页外观

c# - 使用 OpenXml 插入 SQL

c# - 使用 BindingSource 将文本框链接到数据集

c# - Silverlight 5 - 调试 npctrl.dll 崩溃

c# - 在另一台机器上注册 COM dll