这有点难以解释。所以就这样了。
我有这样的功能:
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}}进行枚举?
答案 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>
的情况下,T
为List<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();
}
}