Original Question
I tried to make a generic class for my web requests
internal class Request<TRequest, TResponse>
where TRequest : class
where TResponse : class
{
public Uri RequestUri;
public TRequest RequestItem;
public TResponse ResponseItem;
...
}
The types TRequest
and TResponse
are used to serialize and deserialize with XML, JSON, etc.
Now I want to store those Request<>
in a list for queuing, caching and error handling. So I tried to declare:
private List<Request<object, object>> requests;
But then, I can't add anything to this list of requests because when I try:
var a = new Request<FooRequest, BarResponse>();
requests.Add(a);
I get:
cannot convert from
My.Services.Request<FooRequest,BarResponse>
toMy.Services.Request<object,object>
Is there a way to have a typed list of mixed typed requests (Request<a,b>
and Request<c,d>
)? Or a better approach to handle this?
I'm coding for .NET 4.5 (Windows Phone, Windows Store).
First Edit
After many comments, a couple of unsatisfying answers, and a duplicate vote, I will review below what I tried based on all this:
Using an
interface IRequest<out T, out T1>
Well, interfaces can't have a field. So I would have to cast the item of the list to an instance of
Request<>
to get access to what I want. I'm not sure how to write this cast to retrieve the two variants (TRequest
andTResponse
), help would be appreciated. But by systematically casting, isn't it the same as usingList<object>
in the end?Or I could set properties in the interface. But then it tells me I can't use the keyword
out
:Invalid variance: The type parameter 'TRequest' must be invariantly valid on
My.Services.IRequest<TRequest,TResponse>.RequestItem
. 'TRequest' is covariant.So I could remove the keyword
out
, but when trying to add an item to the list it tells me again:cannot convert from
My.Services.Request<FooRequest,BarResponse>
toMy.Services.IRequest<object,object>
.Using an
abstract class BaseRequest
Almost same issue, my base class can't have a field of type
TRequest
orTResponse
; how to retrieve the two variants? I'll need to cast the item of the list. And soList<object>
would be just as good.Using generics wildcards
Doesn't exist and probably never will.
Check C# Covariance
Too vague answer provided and nothing to achieve it. Not even a link to MSDN. I'm explicitly talking about
List<>
which is a covariant type. Solving this question is not easy as man google.Checking thread: A generic list of anonymous class
Absolutely not a duplicate, as question/answers are not meant for storing to a typed field/property:
var list = new[] { a }.ToList();
This is simply a
List<Request<TRequest, TResponse>>
and unfortunately, I can't declare anything with typevar
in a class, outside of a method.List<object>
orList<dynamic>
Again, not an example of
List<Class<T>>
. But let's tryList<Class<dynamic,dynamic>>
... Failure:cannot convert from
My.Services.Request<TRequest,TResponse>
toMy.Services.Request<dynamic,dynamic>
Checking thread: C# - Multiple generic types in one list
This gets closer to my issue.
-> Interesting, Saeb made the same comment as I regarding most answers with it's all the same as
List<object>
-> Now, Bryan Watts, with the least votes of all answers, found an interesting way to retrieve
TRequest
andTResponse
. He declared this in the interface/baseclass:Type DataType { get; } object Data { get; }
That solves the 'how to retrieve the variants' issue.
First conclusion
As I will need to get the variants, I will need to implement some Type RequestType
and Type ResponseType
. Which means the <TRequest,TResponse>
part will be useless. Which means I will go with a List<Request>
only and the answer to my Stackoverflow question is: no, you can't use/benefit from storing a List<Class<T>>
in C#.
Second Edit
I failed at implementing Bryan Watts solution: apparently it is not possible to use a runtime-type for generic methods. So I can't write Deserialize<request.RequestDataType, request.ResponseDataType>(request)
. So I'm stuck: if I add my requests to a list of requests, I'm having runtime types and I cannot use the generic methods anymore. I didn't know generic methods were so inconvenient.
:(
Second conclusion
I will have a List<Request>
and each Request
will hold an enum
for the kind of request, then I will switch on this enum and write down manually all possible request calls:
switch (@req.RequestType)
{
case RequestType.FirstKindOfRequest:
await Deserialize<FirstKindOfRequest, FirstKindOfResponse>(req);
break;
case RequestType.SecondKindOfRequest:
...
}
That's sadly the only way I could find to hold a list of Class<T>
where T
was supposed to be anything.