C# 如何将具有相同用途但不同输入和输出的类统一到一个接口

C# 如何将具有相同用途但不同输入和输出的类统一到一个接口,c#,generics,abstract-class,C#,Generics,Abstract Class,我需要统一几个具有相同目的但使用相同方法但参数和结果不同的类 你可能觉得奇怪,让我解释一下 我有多个类连接到多个rest/web服务以从中获取数据。在这个例子中,我们假设其余的都是天气预报服务。每个人都做同样的事情。一些地区的回报预测,但他们都在以自己的方式进行。对于每个服务,我都有一个类来实现从中收集数据并映射到我的对象: public class AForecast { public AForecastResult GetForecast (AForecastRequest requ

我需要统一几个具有相同目的但使用相同方法但参数和结果不同的类

你可能觉得奇怪,让我解释一下

我有多个类连接到多个rest/web服务以从中获取数据。在这个例子中,我们假设其余的都是天气预报服务。每个人都做同样的事情。一些地区的回报预测,但他们都在以自己的方式进行。对于每个服务,我都有一个类来实现从中收集数据并映射到我的对象:

public class AForecast
{
    public AForecastResult GetForecast (AForecastRequest request)
    {
         // Grab Forecast
    }
}

public class BForecast
{
    public BForecastResult GetForecast (BForecastRequest request)
    {
         // Grab Forecast
    }
}
我调用这些类,获取预测数据,然后将其映射到我自己的对象中,这很好。我的问题是,现在我有13个预测服务。他们中的许多人使用类似的方法将预测结果映射到我自己的对象中。下一个问题是,现在可能只有我知道如何将新的预测添加到系统中。我想统一这一点,以便能够在Forecast服务实现中添加一些接口,以及一些基本的Forecast映射器

我创建了界面,但由于我不知道预测结果和请求看起来如何,所以它非常通用:

public interface IForecast<out TResult, in TRequest>
{
    TResult GetForecast(TReqiest request)
}
我的
AForecast
实现开始时如下所示:

public class AForecast : IAForecast 
{
    public AForecastResult GetForecast (AForecastRequest request)
    {
         // Grab Forecast
    }
}
正因为如此,我有了Forecast服务,它有自己的接口,有公共的基本接口

问题是当我想在基类中使用它时,基类将能够调用每个预测服务和映射对象:

public abstract ForecastBase
{
    private readonly ?ForecastService _service;
    protected ForecastBase(?ForecastService service)
    {
        _service = service;
    }

    public MapedObject GetForecast(DateTime date, string zip)
    {
        var request = GetRequest(date,zip);
        var forecastServiceResponse = _service.GetForecast(request); 
        return Map(forecastServiceResponse);
    } 

    protected abstract MapedObject Map(?Response response);
    protected abstract ?Request GetRequest(DateTime date, string zip);
}
哦,这太长了。现在,最后一个问题是如何实现
ForeCast
基类?根据我的体系结构,我如何知道将是什么类型的
?ForecastService
?Request
?Response
。我希望我能像这样制作mapper:

public class AMap : ForecastBase 
并且知道在这种情况下,
预报服务
将是
IAForecast
请求
将是
一个预报请求
响应
将是
一个预报响应


如果你需要更多的解释,尽管问吧

我认为您需要的是关键字
where
(不仅如此,它将在这里发挥非常重要的作用)

我为请求和响应定义了如下接口:

public interface IForecastRequest
{

}

public interface IForecastResult
{

}
然后,我们可以继续为预测本身定义接口:

public interface IForecast<out TForecastResult, in TForecastRequest> 
    where TForecastResult : IForecastResult where TForecastRequest : IForecastRequest
{
    TForecastResult GetForecast(TForecastRequest request);
}
这里我们还必须添加
where
s,因为我们必须使用相同的约束调用IForecast接口的通用方法。您可以看到,在构造函数中,我们提供了IForecast实现的一个实例,它将成为我们的服务。这个服务使用了我们在这个基类的定义中也有的两个泛型类型。GetForecast方法现在可以使用IForecast的通用方法和自己的通用方法(GetRequest)来获取MappedObject(我不知道它是什么)。所有类型都对齐,您可以方便地进行IForecastRequest和IForecastResult的IntelliSense和compiletime类型检查

请让我知道我是否正确理解ForecastBase的用途。
我希望这有帮助。我很高兴尝试回答您在这方面的任何问题

编辑:

我希望我能把地图绘制成那样

您显示的方式没有使用任何泛型类型参数。据我所知,如果不指定要使用的结果和请求,您将无法执行此操作。
我为此创建实现的方法如下:

// implement request
public class SomeForecastRequest : IForecastRequest
{

}

// implement result
public class SomeForecastResult : IForecastResult
{

}

// implement forecast itself
public class SomeForecast : IForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeForecastResult GetForecast(SomeForecastRequest request)
    {
        // return the result you got from wherever
    }
}

public class SomeMapper : BaseForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeMapper(IForecast<SomeForecastResult, SomeForecastRequest> service) : base(service)
    {

    }

    protected override SomeForecastRequest GetRequest(DateTime date, string zip)
    {
        // return a request from wherever
    }

    protected override MappedObject Map(SomeForecastResult response)
    {
        // map the response and return it
    }
}
//实现请求
公共类SomeForecastRequest:IForecastRequest
{
}
//实施结果
公共类SomeForecasterResult:IforeCasterResult
{
}
//实现预测本身
公共类预测:IForecast
{
公共SomeForecastResult GetForecast(SomeForecastRequest请求)
{
//返回从何处获得的结果
}
}
公共类映射器:BaseForecast
{
公共SomeMapper(IForecast服务):基本(服务)
{
}
受保护的覆盖SomeForecastRequest GetRequest(日期时间日期,字符串zip)
{
//从任何地方返回请求
}
受保护的覆盖映射对象映射(SomeForecasterResult响应)
{
//映射响应并返回它
}
}
编辑2: 我刚刚读了你的评论,内容如下:

// implement request
public class SomeForecastRequest : IForecastRequest
{

}

// implement result
public class SomeForecastResult : IForecastResult
{

}

// implement forecast itself
public class SomeForecast : IForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeForecastResult GetForecast(SomeForecastRequest request)
    {
        // return the result you got from wherever
    }
}

public class SomeMapper : BaseForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeMapper(IForecast<SomeForecastResult, SomeForecastRequest> service) : base(service)
    {

    }

    protected override SomeForecastRequest GetRequest(DateTime date, string zip)
    {
        // return a request from wherever
    }

    protected override MappedObject Map(SomeForecastResult response)
    {
        // map the response and return it
    }
}
。。它们之间没有联系。这些对象没有自己的接口,我也没有将其包装在自己的接口中

如果希望保持这种方式,则无法为映射器创建基类。映射程序需要知道,
AForecast
有一个名为
GetForecast
的方法。如果没有,它将如何称呼它。如果你尝试这个,你将陷入编译错误的境地。您需要告诉编译器“如果这个类有
GetForecast
-方法,它可以处理任何类”,否则它将拒绝尝试调用
GetForecast
。告诉编译器这一点的方法是说“伙计,看,我得到了这个很棒的接口,它有
GetForecast
-方法。我只允许调用者使用实现这个接口的类,这样你就可以确保
GetForecast
-方法存在。好吗?”。这就是为什么您要使用
where
关键字,我感谢您提出的问题,因为这是一个非常好(但不太容易)的示例,说明了如何使用它

编辑3:
顺便说一句,没有什么可以阻止您使用类而不是接口来执行
IForecastRequest
IForecastResult
(当然您会更改名称,但其他一切都可以保持不变)。我不知道您的请求和响应对象应该做什么/存储什么,所以我不知道在接口上使用类是否适合您。
我只是想说这也是可能的

我很想听听你的反馈:)

你能吗
// implement request
public class SomeForecastRequest : IForecastRequest
{

}

// implement result
public class SomeForecastResult : IForecastResult
{

}

// implement forecast itself
public class SomeForecast : IForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeForecastResult GetForecast(SomeForecastRequest request)
    {
        // return the result you got from wherever
    }
}

public class SomeMapper : BaseForecast<SomeForecastResult, SomeForecastRequest>
{
    public SomeMapper(IForecast<SomeForecastResult, SomeForecastRequest> service) : base(service)
    {

    }

    protected override SomeForecastRequest GetRequest(DateTime date, string zip)
    {
        // return a request from wherever
    }

    protected override MappedObject Map(SomeForecastResult response)
    {
        // map the response and return it
    }
}