C# 将混凝土类型转换为通用类型?

C# 将混凝土类型转换为通用类型?,c#,generics,C#,Generics,我正在尝试编译以下代码: public class BaseRequest<TResponse> where TResponse : BaseResponse {} public class BaseResponse {} public class FooRequest : BaseRequest<FooResponse> {} public class FooResponse : BaseResponse {} ... public TResponse MakeR

我正在尝试编译以下代码:

public class BaseRequest<TResponse> where TResponse : BaseResponse {}
public class BaseResponse {}

public class FooRequest : BaseRequest<FooResponse> {}
public class FooResponse : BaseResponse {}

...

public TResponse MakeRequest<TResponse>(BaseRequest<TResponse> request)
     where TResponse : BaseResponse
{
}
但是,
***
行不会编译,因为编译器不知道
FooResponse
treResponse
。我知道这是因为它是在
FooRequest
中指定的。有没有办法在不涉及恶劣反射的情况下解决这个问题(在这种情况下,我宁愿返回
BaseResponse

谢谢


更新:我正在使用泛型来强制执行返回类型,以便调用站点确切地知道预期的内容。在这里只返回
BaseResponse
要容易得多,但它把确定具体返回类型的负担交给了调用方,而不是请求处理程序(请求处理程序当然知道所有的输入)。

正如我在评论中所说的,我怀疑你做得不对。这看起来像是滥用仿制药

也就是说,您告诉编译器“我知道的类型信息比您知道的更多”的方式是通过强制转换

var response = new FooResponse(...);   
return (TResponse)(object)response;

转换到对象,然后转换到树响应,告诉编译器“我知道从响应到树响应有一个标识、取消装箱或引用转换”。如果你错了,你会在运行时得到一个异常。

在我看来,你应该从非泛型版本
BaseRequest
派生你的
BaseRequest
类,然后将你的函数编写为:

public BaseResponse MakeRequest(BaseRequest request)
在我看来,这似乎是正确的方法,因为您甚至没有引用函数中的类型

在我看来,泛型在这里只是作为句法上的糖分。以这种方式编写函数的好处是能够编写:

FooResponse r = MakeRequest(new FooRequest(...))
与此相反:

FooResponse r = (FooResponse)MakeRequest(new FooRequest(...))
因此,好处并不巨大。事实上,您被自己的代码弄糊涂了,以至于看不到缺少的东西是强制转换,这意味着代码可能比非泛型方法更不清晰

哦,你的方法的另一个缺点是你将失去做以下事情的能力:

var requests = new List<BaseRequest> { new FooRequest(), new BarRequest() };
var responses = new List<BaseResponse> ();
foreach(var request in requests)
{
    responses.Add(MakeRequest(request));
}
var requests=newlist{new FooRequest(),new BarRequest()};
var responses=newlist();
foreach(请求中的var请求)
{
添加(MakeRequest(request));
}
或者你可以做的是:

public BaseResponse MakeRequest(BaseRequest request) { /* thing that does the work */ }
public TResponse MakeRequest<TResponse>(BaseRequest<TResponse> request)
{
    // Just for the nice syntax
    return (TResponse)MakeRequest(request);
}
public-BaseResponse-MakeRequest(BaseRequest-request){/*完成工作的东西*/}
公共响应MakeRequest(BaseRequest请求)
{
//只是为了好的语法
返回(响应)MakeRequest(请求);
}

但这看起来真的很复杂。无论如何,我将首先让您思考一下,该方法不应该被称为MakeResponse吗,因为它返回一个treresponse?但更一般地说:如果你必须检查一个事物的类型,并且你对某个特定类型采取了一些特定的操作,那么你首先就不是在编写泛型代码,那么为什么你要使用泛型呢?如果您有特殊的逻辑,知道如何将FooRequest转换为FooResponse,那么就创建一个接受FooRequest并返回FooResponse的方法;不需要泛型。@埃里克-我正在研究一种责任链模式,其中路由节点只是传递消息向前,也许中间的某个节点识别请求并返回正确的响应。不过,我应该在代码示例中明确说明。@forcey CoR并不意味着任何关于泛型的内容,而匹配实例类型并不是泛型的目的。只需传递BaseRequests并使用
if(request is FooRequest){…}
@ChrisShain和泛型,调用站点的类型将更强大:
fooressresponse=chain.MakeRequest(new FooRequest)
。换句话说,在消息处理程序中而不是在调用站点中强制转换
BaseResponse
->
FooResponse
。谢谢-看起来我可以将FooResponse强制转换为BaseResponse,然后再转换为treResponse。我仍然认为我没有滥用它:)@forcey,泛型应该是泛型的。如果在处理泛型参数时进行强制转换,则很可能滥用系统。@RomanR.-公平地说,我意识到我只是在使用泛型提供的类型推断系统,而不是按照它应该使用的方式使用泛型。非常感谢,这确实非常有用。
public BaseResponse MakeRequest(BaseRequest request) { /* thing that does the work */ }
public TResponse MakeRequest<TResponse>(BaseRequest<TResponse> request)
{
    // Just for the nice syntax
    return (TResponse)MakeRequest(request);
}