Home > Blockchain >  C# How to pass generic enumerable type parameter to another method expecting an IEnumerable?
C# How to pass generic enumerable type parameter to another method expecting an IEnumerable?

Time:12-28

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.

  •  Tags:  
  • c#
  • Related