Reputation: 6790
I have a class that accepts T
and has a method that takes an HttpResponseMessage
and modifies the content in the response.
public class MyClass<T> {
HttpResponseMessage Modify(HttpResponseMessage response) {
T content;
if(response.TryGetContentValue(out content)) {
DoSomethingWithContent(content);
}
return response;
}
public void DoSomethingWithContent(T content) {}
public void DoSomethingWithContent(IEnumerable<T> content) {}
}
Used like this...
This works as long as the response content value is of type T
, but sometimes it's IEnumerable<T>
, and when that's the case TryGetContentValue()
will return false since T
is not IEnumerable<T>
. So I created an overload for DoSomethingWithContent
but I'm struggling to figure out an efficient way to dynamically cast or declare content
to the proper type so the correct overload is called.
Answer
I went with recursive
s answer but I wanted to post the complete method for reference:
public class MyClass<T> {
HttpResponseMessage Modify(HttpResponseMessage response) {
object content;
if(response.TryGetContentValue(out content)) {
if(content is IEnumerable<T>)
DoSomethingWithContent((IEnumerable<T>)content);
else
DoSomethingWithContent((T)content);
}
return response;
}
public void DoSomethingWithContent(T content) {}
public void DoSomethingWithContent(IEnumerable<T> content) {}
}
Upvotes: 2
Views: 2225
Reputation: 85625
Assuming DoSomething
is the same thing for IEnumerable<T>
and T
(eg., that one delegates to the other) - I think it'd make more sense to just always use an IEnumerable<T>
- after all, a T
is just an IEnumerable<T>
with 1 value.
Of course, that sidesteps the problem that TryGetContentValue
will only ever give you back a T
- not an IEnumerable<T>
. You could handle that by first trying T
and then fall back to IEnumerable<T>
. I think pulling it out as an object and handling the cast yourself may work as well.
So - I think you'll end up with something like:
public class MyClass<T> {
HttpResponseMessage Modify(HttpResponseMessage response) {
IEnumerable<T> content = this.GetContent(response);
DoSomethingWithContent(content);
return response;
}
private IEnumerable<T> GetContent(HttpResponseMessage response) {
object content;
if (!response.TryGetContentValue(out content)) return new T[] { };
if (content is T) return new T[] { (T)content };
return content as IEnumerable<T> ?? new T[] { };
}
public void DoSomethingWithContent(IEnumerable<T> content) {
foreach (var t in content) {
// blah, blah
}
}
}
If DoSomethingWithContent(T)
and DoSomethingWithContent(IEnumerable<T>)
are in fact different - then you'd basically follow the same pattern as GetContent
to make the branching decision.
Upvotes: 1
Reputation: 7602
This work out?
HttpResponseMessage Modify(HttpResponseMessage response) {
T content;
if(response.TryGetContentValue<T>(out content)) {
DoSomethingWithContent(content);
}
else
{
IEnumerable<T> content;
if(response.TryGetContentValue<IEnumerable<T>>(out content)) {
DoSomethingWithContent(content);
}
}
return response;
}
Upvotes: 1
Reputation: 52107
The IEnumerable<out T>
is covariant, so if you just...
var enumerable = content as IEnumerable<T>;
if (enumerable == null)
DoSomethingWithContent(content);
else
DoSomethingWithContent(enumerable);
...it will still call DoSomethingWithContent(IEnumerable<T> content)
even if the actual run-time type of content
is IEnumerable<C>
, where C
inherits T
.
This doesn't address why you are trying "fit" IEnumerable<T>
in T
in the first place?
Upvotes: 2
Reputation: 48076
Yeah there is circularity problem with your design. If MyClass<T>
is initialized as for example MyClass<List<string>>
then T = List<string>
so the overloaded definition won't actually get invoked. What you actually need to do is use only a single method but have a clause to test if T
implements IEnumerable<T>
. You can do this like;
if (content is IEnumerable<T>)
I'm not entirely sure that will work though because you may even have to specify the type the IEnumberable uses. If that's the case then check if it implements the non-generic IEnumberable because all collections that implement the generic version also implement the non-generic version.
Upvotes: 1
Reputation: 86064
You can use is
to determine which case to use.
if (content is IEnumerable<T>)
DoSomethingWithContent((IEnumerable<T>)content);
else
DoSomethingWithContent(content);
If you're feeling sassy, you could just use the runtime binder by casting to dynamic, but that's not recommended.
DoSomethingWithContent((dynamic)content);
Upvotes: 7