开发者

How can I enumerate through an object of type `T` when it is a IEnumerable

开发者 https://www.devze.com 2023-03-12 10:33 出处:网络
This is a bit difficult to explain. So here it goes. I have a function like this: public T FooBar<T>(Func<T> function)

This is a bit difficult to explain. So here it goes.

I have a function like this:

public T FooBar<T>(Func<T> function)
{
   T returnData = function();

   // want to iterate through returnData to do something to it

   return returnData;
}

If the returnData (T) is an IEnumerable list, then I would like to enum开发者_如何学Goerate through returnData to modify its contents using reflection. But I can't seem to be able to do it. When I try to cast returnData to an enumerable type, I get an exception:

Unable to cast object of type

'System.Collections.Generic.List`1[Cars]'

to type

'System.Collections.Generic.List`1[System.Object]'.

I will not know that the return type will be a list of 'cars' for example ahead of time, only at run time. So I have to check using reflection if it is a list, and then try to cast it so that I can enumerate through it.

Unless I am going about it the wrong way. How can I enumerate through returnData if it is of type T?


One approach is to add a type constraint on T, but this is not ideal:

public T FooBar<T>(Func<T> function) where T : IEnumerable
{
    // T is not strongly typed for the enumerated item

If you changed your method slightly (w.r.t. T):

public IEnumerable<T> FooBar<T>(Func<IEnumerable<T>> function)

Then you have strong typing on the actual item being enumerated with the added bonus of accepting enumerable objects.


So I noticed from a second read of your question, there is some confusion about what T means for your variable returnData. In the case where FooBar() is passed a List<Car>, T is List<Car>, and really has no association with the generic type specification of the List<> itself. You can think of it as some List<U> where U is some other, unknown type.

At runtime you will have no simple way to get to U as it is hidden, so to speak, inside T. You could use overloading as some of the other answerers recommend, and provide a non-IEnumerable<U> method and one which takes arguments of type Func<IEnumerable<T>>.

Perhaps with some more details about the goal of FooBar<T> we could make some more specific recommendations.


if (returnData is System.Collections.IEnumerable)
{
   foreach (object o in (System.Collections.IEnumerable)returnData)
   {
      // Do something.
   }
}

Really, though, why not have an additional overload like this:

public T FooBar<T>(Func<IEnumerable<T>> function) 


Have you tried type casting to IEnumerable instead of IEnumerable<T>? With IEnumerable you can still use it in a foreach loop. The variable each item would go in should be of type object i.e.:

foreach(object item in (IEnumerable)T){...}

You should check first to be sure that T implements IEnumerable.


The issue here is IEnumerable and IEnumerable Of T are not the same... but you can check for the difference and account for it in your code. Note that IEnumerable Of T inherits IEnumerable, so you can wrap the check for the generic version inside the non-generic version.

The following worked for me in a small test I wrote - I hope it is sufficient for you to do what you need.

Here is the meat and potatoes:

class FooBarOfT
{
    public T FooBar<T>(Func<T> function)
    {
        T returnData = function();

        //Want to iterate through returnData to do something to it.
        if (returnData is IEnumerable) 
        {
            // get generic type argument
            var returnDataType = returnData.GetType();

            if (returnDataType.IsGenericType)
            {
                // this is a System.Collections.Generic.IEnumerable<T> -- get the generic type argument to loop through it
                Type genericArgument = returnDataType.GetGenericArguments()[0];

                var genericEnumerator =
                    typeof(System.Collections.Generic.IEnumerable<>)
                        .MakeGenericType(genericArgument)
                        .GetMethod("GetEnumerator")
                        .Invoke(returnData, null);

                IEnumerator enm = genericEnumerator as IEnumerator;
                while (enm.MoveNext())
                {
                    var item = enm.Current;
                    Console.WriteLine(string.Format("Type : {0}", item.GetType().Name));
                }

            }
            else
            {
                // this is an System.Collections.IEnumerable (not generic)
                foreach (var obj in (returnData as IEnumerable))
                {
                    // do something with your object
                }
            }
        }

        return returnData;
    }
}

I also set up some supporting test classes:

class Foo
{
    private string _fooText;

    public Foo(string fooText)
    {
        _fooText = fooText;
    }
    public string Execute()
    {
        return string.Format("executed! with {0} !", _fooText);
    }
}

class Bar
{
    public string BarContent { get; set; }
}

And a small console app to run some tests:

class Program
{
    static void Main(string[] args)
    {
        // tests
        Func<string> stringFunc = () => 
            "hello!";

        Func<List<Foo>> listFooFunc = () => 
            new List<Foo> 
            { 
                new Foo("Hello!"),
                new Foo("World!")
            };

        Func<IEnumerable> ienumerableFooFunc = () =>
            new Hashtable
            {
                { "ItemOne", "Foo" },
                { "ItemTwo", "Bar" }
            };


        var fooBarOfT = new FooBarOfT();

        fooBarOfT.FooBar(stringFunc);
        fooBarOfT.FooBar(listFooFunc);
        fooBarOfT.FooBar(ienumerableFooFunc);

        Console.ReadKey();
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消