将混凝土类型浇筑成通用类型?

时间:2012-02-23 19:41:45

标签: c# generics

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

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
{
}

我希望我可以致电MakeRequest(new FooRequest())并将返回值设为FooResponse。被调用者不必知道FooRequest并且可以将其传递给另一个处理程序。签名工作正常,但我无法实现MakeRequest方法。如果我实现它:

public TResponse MakeRequest<TResponse>(BaseRequest<TResponse> request)
     where TResponse : BaseResponse
{
    FooRequest fooRequest = request as FooRequest;
    if (fooRequest != null)   // if I can handle the request, handle it
    {
        return new FooResponse(...);   // ***
    }

    BarRequest barRequest = request as BarRequest;
    if (barRequest != null) 
    {
        return new BarResponse(...);  
    }

    else                      // otherwise, pass it on to the next node
    {
        // maybe it will handle a BazRequest, who knows
        return nextNode.MakeRequest(request);
    }
}

但是***行不会编译,因为编译器不知道FooResponseTResponse。我知道这是因为它在FooRequest中指定。有没有办法解决这个问题,而不涉及讨厌的反思(在这种情况下,我宁愿返回BaseResponse)?

感谢。

更新:我正在使用泛型来强制执行返回类型,因此调用网站确切地知道会发生什么。在这里返回BaseResponse要容易得多,但是它将具体的返回类型的负担放在调用者而不是请求处理程序(当然知道关于键入的所有内容)。

2 个答案:

答案 0 :(得分:8)

正如我在评论中所说,我怀疑你做错了。这看起来像滥用泛型。

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

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

转换为对象然后转向TResponse告诉编译器“我知道从响应到TResponse有一个标识,拆箱或引用转换”。如果你错了,你会在运行时得到一个例外。

答案 1 :(得分:2)

在我看来,您应该从非通用版本BaseRequest<T>派生您的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));
}

或者你可以做的是:

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);
}

但这看起来真的很复杂。无论如何,我会让你反思那个