I have a method that accepts a basic generic variable in the method signature:
public void Foo<T>(T val) {
if (val is IEnumerable) {
Bar(val)
}
}
...Logic if not enumerable
The signature for Bar looks like this:
private void Bar<T>(IEnumerable<T> vals) {
...Logic if enumerable
}
The logic is very lengthy and different based on whether the item passed in is enumerable or not. I can't get Bar(val)
to work. I tried the following, let me know what I'm missing:
Bar(val)
Bar((IEnumerable<T>)val)
(Compiles, but results in this error at run-time when tried with a List of ints:
Unable to cast object of type 'System.Collections.Generic.List`1[System.Int32]' to type 'System.Collections.Generic.IEnumerable`1[System.Collections.Generic.List`1[System.Int32]]'.'
Bar((IEnumerable)val)
CodePudding user response:
The only option without changing the signature and/or using dynamic
is to dive into reflection. You need to get the closed generic type of IEnumerable<TInner>
(where T : IEnumerable<TInner>
), construct corresponding closed version of Bar
and invoke it. Something along this lines:
void Foo<T>(T val)
{
var genericEnumInterface = typeof(T).GetInterfaces()
.FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (genericEnumInterface is not null)
{
MethodInfo mi = ...; // get Bar via reflection
var constructedMethod = mi.MakeGenericMethod(genericEnumInterface);
constructedMethod.Invoke(this, new[]{(object)val}); // assuming Bar is in the same class as Foo
}
}
Note that there are types which will implement IEnumerable<T>
which you possibly will not want to process via Bar
like string
which is IEnumerable<char>
. Another concern is that reflection can cause noticable performance hit in some cases so you can try caching
it (see for example this or this answer).
Also personally I would try to avoid such generic method as Foo
due to possible misuse and corner cases like string
.
CodePudding user response:
If you don't want to go down the reflection route there is another option:
if (val is IEnumerable)
{
Bar(val as dynamic);
}
You may not want to go down that rabbit hole though.