这有点难以解释。所以它开始了。
我有一个这样的功能:
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[Cars]'
键入
'System.Collections.Generic.List`1[System.Object]'.
我不知道返回类型会是一个"汽车"列表,例如提前,只在运行时。所以我必须使用反射来检查它是否是一个列表,然后尝试对其进行强制转换,以便我可以通过它进行枚举
除非我走错了路。如果它是T
类型的,我如何通过returnData
进行枚举?
一种方法是在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>
的情况下,T
是List<Car>
,并且实际上与List<>
本身的通用类型规范没有关联。你可以把它想象成某种List<U>
,其中U
是另一种未知类型。
在运行时,您将无法简单地访问U
,因为它隐藏在T
中。您可以按照其他一些回答者的建议使用重载,并提供一个非IEnumerable<U>
方法和一个接受Func<IEnumerable<T>>
类型参数的方法。
也许有了更多关于FooBar<T>
目标的细节,我们可以提出一些更具体的建议。
if (returnData is System.Collections.IEnumerable)
{
foreach (object o in (System.Collections.IEnumerable)returnData)
{
// Do something.
}
}
不过,真的,为什么不增加一个这样的过载呢:
public T FooBar<T>(Func<IEnumerable<T>> function)
您是否尝试过将类型转换为IEnumerable
而不是IEnumerable<T>
?使用IEnumerable
,您仍然可以在foreach
循环中使用它。每个项目将进入的变量应为object
类型,即:
foreach(object item in (IEnumerable)T){...}
您应该首先检查以确保T实现了IEnumerable
。
这里的问题是IEnumerable和IEnumerableOf T不一样。。。但是您可以检查差异并在代码中说明。请注意,IEnumerableOfT继承了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();
}
}