我可以有/使用一个字段/属性与列表>T可以是任意值
本文关键字:列表 Class 任意值 字段 我可以 一个 属性 | 更新日期: 2023-09-27 18:04:41
原题
我试图为我的web请求创建一个通用类
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
:无效方差:类型参数'TRequest'必须在
My.Services.IRequest<TRequest,TResponse>.RequestItem
上始终有效。"请求"是协变的。所以我可以删除关键字
out
,但是当试图添加一个项目到列表时,它又告诉我:不能从
My.Services.Request<FooRequest,BarResponse>
转换为My.Services.IRequest<object,object>
。使用
abstract class BaseRequest
几乎相同的问题,我的基类不能有
TRequest
或TResponse
类型的字段;如何检索这两个变体?我需要对列表中的项进行强制转换。所以List<object>
也一样好。使用泛型通配符
不存在,可能永远也不会存在。
检查c#协方差
提供的答案太模糊,没有什么可以实现。连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# -多个泛型类型在一个列表
这更接近我的问题了。
->有趣,Saeb对大多数的答案做出了与我相同的评论,这与
完全相同List<object>
->现在,Bryan Watts找到了一种有趣的方法来检索
TRequest
和TResponse
,他在所有答案中获得的票数最少。他在接口/基类中声明:Type DataType { get; } object Data { get; }
解决了"如何检索变量"的问题。
第一个结论
因为我需要得到的变体,我将需要实现一些Type RequestType
和Type ResponseType
。这意味着<TRequest,TResponse>
部分将是无用的。这意味着我将只使用List<Request>
,我的Stackoverflow问题的答案是:不,你不能使用/受益于在c#中存储List<Class<T>>
。
第二个编辑
我在实现Bryan Watts的解决方案时失败了:显然,不可能为泛型方法使用运行时类型。所以我不能写Deserialize<request.RequestDataType, request.ResponseDataType>(request)
。所以我卡住了:如果我把请求添加到请求列表中,我就有了运行时类型,我就不能再使用泛型方法了。我不知道泛型方法这么不方便。(
第二结论
我将有一个List<Request>
,每个Request
将为请求类型保留一个enum
,然后我将打开此enum并手动写下所有可能的请求调用:
switch (@req.RequestType)
{
case RequestType.FirstKindOfRequest:
await Deserialize<FirstKindOfRequest, FirstKindOfResponse>(req);
break;
case RequestType.SecondKindOfRequest:
...
}
遗憾的是,这是唯一的方法,我可以找到持有Class<T>
的列表,其中T
应该是任何东西。
这是一个典型的情况。我之前也问过很多次。我看到两种方法
->明显的选择是有一个非泛型基类/接口,它可以解释所有的请求类型,然后携带List<NonGenericRequest>
。
接口不能有字段。因此,我必须将列表的项强制转换为
Request<>
的实例才能访问我想要的内容。
不要使用字段。想都别想。切换到属性。我必须重申这一点。接口——实际上是所有的公共api——处理属性。
问题是属性的设置。它破坏了类型安全。更多信息请看这个帖子。或者我可以在接口中设置属性。但是它告诉我不能使用关键字
out
到目前为止,您可以编写类似这意味着
<TRequest,TResponse>
部分将是无用的。这意味着我将只使用列表,我的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
类型接口。
您可以通过提取一个可变的IRequest
接口来利用接口差异:
interface IRequest<out T, out T1>
{
}
并在你的Request类中实现它:
class Request<TRequest, TResponse> : IRequest<TRequest, TResponse>
where TRequest : class
where TResponse : class
这将允许您创建一个可以存储任意IRequest
实例的List<IRequest< object, object>>
:
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>());
我认为泛型通配符可以帮助你:http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2633766-wildcard-generic
由于这至少在不久的将来不会发生,我看到三个选项:
- 你可以投票,等待,并希望微软在泛型中采用通配符…
- 正如Mrida所评论的,你可以将你的代码转换为使用接口,然后使用c#协方差。
- 如果协方差不是一个可接受的解决方案,你可以定义一个非泛型基类
RequestBase
,只有非泛型的功能,并使RequestBase
列表(编辑:我认为这是HenkHolterman的评论)。或者,如果您不想更改Request
类,您可以使用非泛型功能定义非泛型包装器。