我试图为我的网络请求制作一个通用类
internal class Request<TRequest, TResponse>
where TRequest : class
where TResponse : class
{
public Uri RequestUri;
public TRequest RequestItem;
public TResponse ResponseItem;
...
}
类型TRequest
和TResponse
用于使用XML,JSON等序列化和反序列化。
现在我想将这些Request<>
存储在列表中,以便进行排队,缓存和错误处理。所以我试着宣布:
private List<Request<object, object>> requests;
但是,我无法在此请求列表中添加任何内容,因为当我尝试时:
var a = new Request<FooRequest, BarResponse>();
requests.Add(a);
我明白了:
无法从
My.Services.Request<FooRequest,BarResponse>
转换为My.Services.Request<object,object>
是否有办法获得混合类型请求的类型列表(Request<a,b>
和Request<c,d>
)?或者更好的方法来解决这个问题?
我正在编写.NET 4.5(Windows Phone,Windows Store)。
经过多次评论,一些不满意的答案以及重复的投票后,我将在下面回顾一下基于这一切的尝试:
使用interface IRequest<out T, out T1>
嗯,界面不能有一个字段。所以我必须将列表的项目强制转换为Request<>
的实例才能访问我想要的内容。我不确定如何编写此演员来检索两个变体(TRequest
和TResponse
),我们将不胜感激。但是通过系统地投射,它与最终使用List<object>
是否相同?
或者我可以在界面中设置属性。但它告诉我,我无法使用关键字out
:
无效方差:类型参数&#39; TRequest&#39;必须在
My.Services.IRequest<TRequest,TResponse>.RequestItem
上无条件有效。 &#39; TRequest&#39;是协变的。
所以我可以删除关键字out
,但在尝试将项目添加到列表时,它再次告诉我:
无法从
My.Services.Request<FooRequest,BarResponse>
转换为My.Services.IRequest<object,object>
。
使用abstract class BaseRequest
几乎相同的问题,我的基类不能有TRequest
或TResponse
类型的字段;如何检索这两个变种?我需要投射列表中的项目。所以List<object>
也会一样好。
使用泛型通配符
不存在,可能永远不会存在。
检查C#协方差
提供的答案太模糊,没有什么可以实现的。甚至不是link to MSDN。我明确地谈到了List<>
这是一种协变类型。解决这个问题并不容易,因为 man google 。
检查主题:匿名类的通用列表
绝对不重复,因为问题/答案不适用于存储到打字字段/属性:
var list = new[] { a }.ToList();
这只是一个List<Request<TRequest, TResponse>>
,遗憾的是,我无法在方法之外的类中声明类型为var
的任何内容。
List<object>
或List<dynamic>
同样,不是List<Class<T>>
的示例。但是,让我们试试List<Class<dynamic,dynamic>>
......失败:
无法从
My.Services.Request<TRequest,TResponse>
转换为My.Services.Request<dynamic,dynamic>
检查主题: C# - Multiple generic types in one list
这更接近我的问题。
- &GT;有趣的是, Saeb 在same comment和大多数答案中取得了it is not possible to use a runtime-type for generic methods 它与 List<object>
- &GT;现在, Bryan Watts ,所有答案投票率最低,找到了一种有趣的方法来检索TRequest
和TResponse
。他在interface / baseclass中声明了这个:
Type DataType { get; }
object Data { get; }
这解决了如何检索变种&#39;问题。
由于我需要获取变体,因此我需要实现一些Type RequestType
和Type ResponseType
。这意味着<TRequest,TResponse>
部分将毫无用处。这意味着我只使用List<Request>
,而我的Stackoverflow问题的答案是:不,您无法使用/受益于在C#中存储List<Class<T>>
我未能实施Bryan Watts解决方案:显然{{3}}。所以我不能写Deserialize<request.RequestDataType, request.ResponseDataType>(request)
。所以我陷入了困境:如果我将我的请求添加到请求列表中,我就有了运行时类型,我再也不能使用泛型方法了。我不知道通用方法是如此不方便。
:(
我将有一个List<Request>
,每个Request
会为请求提供一个enum
,然后我会打开此枚举并手动记下所有可能的请求调用:< / p>
switch (@req.RequestType)
{
case RequestType.FirstKindOfRequest:
await Deserialize<FirstKindOfRequest, FirstKindOfResponse>(req);
break;
case RequestType.SecondKindOfRequest:
...
}
遗憾的是,我能找到Class<T>
T
列表{{1}}应该是什么的唯一方法。
答案 0 :(得分:2)
这是一个经典的情况。之前也问过很多次。我看到两种方法。
- &GT;显而易见的选择是拥有一个非泛型基类/接口,它可以解释您的所有请求类型,然后随身携带List<NonGenericRequest>
。
- &GT;第二种方法是利用协方差。我看到你面临一些问题。我将尝试一个接一个地解决它:
好吧,接口不能有字段。所以我必须将列表的项目强制转换为
Request<>
的实例才能访问我想要的内容。
不要使用字段。甚至不要想到它。切换到属性。我必须重申这一点。接口 - 实际上是所有公共API - 处理属性。
或者我可以在界面中设置属性。但后来它告诉我我不能使用关键字
out
问题是您的媒体资源的 setters 。它打破了类型安全。有关详情,请参阅this thread。
这意味着
<TRequest,TResponse>
部分将毫无用处。这意味着我只使用List,而Stackoverflow问题的答案是:不,你不能使用/受益于在C#中存储List<Class<T>>
。
到目前为止,您可以编写类似var requests = new List<IRequest<object, object>>()
的内容。但这意味着您不会获得强类型TRequest
和TResponse
。解决方案是摆脱泛型类型参数TRequest
和TResponse
(即约束where T : class
)并包含更可用的约束。 对您有用的东西。
将它们全部结合起来,我们走了:
interface IMessage //I will call it IMessage for now
{
//implement whatever that make deserializing possible
}
class FooRequest : IMessage
{
}
class BarResponse : IMessage
{
}
interface IRequest<out TRequest, out TResponse>
where TRequest : IMessage
where TResponse : IMessage
{
TRequest RequestItem { get; }
TResponse ResponseItem { get; }
//whatever fits, but only getters for out parameter types
}
class Request<TRequest, TResponse> : IRequest<TRequest, TResponse>
where TRequest : IMessage
where TResponse : IMessage
{
//you can extend interface properties with setters here
}
现在你可以写:
var requests = new List<IRequest<IMessage, IMessage>>();
var a = new Request<FooRequest, BarResponse>();
requests.Add(a);
foreach (var request in requests)
{
Deserialize(request);
}
void Deserialize<TRequest, TResponse>(IRequest<TRequest, TResponse> request)
where TRequest : IMessage
where TResponse : IMessage
{
//have access to request.RequestItem, request.ResponseItem etc
}
注意:为简单起见,我假设TRequst
和TResponse
都是相似的类型,因此它们都是从IMessage
演变而来的。您可以为它们分别设置IRequest
和IResponse
类型接口。
答案 1 :(得分:1)
我认为泛型通配符可以帮助你: http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2633766-wildcard-generic
由于这种情况至少在不久的将来不会发生,我认为有三种选择:
RequestBase
,并列出RequestBase
(编辑:我认为这是HenkHolterman的评论)。或者,如果您不想更改Request
类,则可以使用非通用功能定义非通用包装。答案 2 :(得分:1)
您可以通过提取变体IRequest
接口来利用接口差异:
interface IRequest<out T, out T1>
{
}
并在Request类中实现它:
class Request<TRequest, TResponse> : IRequest<TRequest, TResponse>
where TRequest : class
where TResponse : class
这将允许您创建可以存储任意List<IRequest< object, object>>
个实例的IRequest
:
var s = new List<IRequest< object, object>>();
s.Add(new Request<c1, c2>());
如果您想要某些特定于类型的行为,您仍然必须将列表的内容强制转换为更具体的类。这可能会或可能不会影响高流量情况下的性能。
另一种选择是存储请求的字符串内容,或将它们提取到抽象基类。这个抽象类的列表对于缓存和日志记录来说已经足够了。你可以这样:
abstract class BaseRequest
{
public string RequestContent { get; protected set; }
public string ResponseContent { get; set; }
}
class Request<TRequest, TResponse> : BaseRequest
where TRequest : class
where TResponse : class
你仍然可以写:
var s = new List<BaseRequest>();
s.Add(new Request<c1, c2>());