当它是IEnumerable时,如何通过类型为“T”的对象进行枚举

时间:2011-06-13 23:19:15

标签: c# reflection

这有点难以解释。所以就这样了。

我有这样的功能:

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

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

   return returnData;
}

如果returnData(T)是IEnumerable列表,那么我想通过returnData枚举来使用反射修改其内容。但我似乎无法做到这一点。当我尝试将returnData转换为可枚举类型时,我得到一个例外:

  

无法转换

类型的对象      

'System.Collections.Generic.List`1 [汽车]'

     

输入

     

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

我不会知道返回类型将是一个“汽车”列表,例如提前,仅在运行时。所以我必须检查使用反射,如果它是一个列表,然后尝试投射它,以便我可以枚举它。

除非我采取错误的方式。如果returnData类型为T,我如何通过{{1}}进行枚举?

4 个答案:

答案 0 :(得分:6)

一种方法是在T上添加类型约束,但这并不理想:

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

如果您稍微改变了方法(w.r.t。T):

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

然后你在枚举的实际项目上有强大的输入,并附加了接受可枚举对象的额外奖励。


因此,我在第二次阅读您的问题后发现,对T对您的变量returnData的含义存在一些疑惑。在FooBar()传递List<Car>的情况下,TList<Car>,并且与List<>本身的泛型类型规范实际上没有任何关联。您可以将其视为某些List<U>,其中U是其他未知类型。

在运行时,您将无法获得U的简单方法,因为它隐藏在T内部。您可以像其他一些回答者推荐的那样使用重载,并提供非IEnumerable<U>方法和一个带有Func<IEnumerable<T>>类型参数的方法。

也许有关FooBar<T>目标的更多细节,我们可以提出一些更具体的建议。

答案 1 :(得分:4)

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

但是,真的,为什么没有像这样的额外重载:

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

答案 2 :(得分:2)

您是否尝试过输入IEnumerable而不是IEnumerable<T>?使用IEnumerable,您仍然可以在foreach循环中使用它。每个项目的变量应该是object类型,即:

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

您应首先检查以确保T实现IEnumerable

答案 3 :(得分:1)

这里的问题是IEnumerable和IEnumerable Of T不一样......但你可以在代码中检查差异并对其进行说明。请注意,IEnumerable Of T继承了IEnumerable,因此您可以在非泛型版本中包含对泛型版本的检查。

在我写的一个小测试中,以下对我有用 - 我希望你做你需要的就足够了。

这是肉和土豆:

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;
    }
}

我还设置了一些支持测试类:

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; }
}

用于运行某些测试的小型控制台应用程序:

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();
    }
}