In the following code I want to return an IEnumerable without creating a new data structure object. However, I get a compiler error with the following code. What am I missing?
Error Cannot implicitly convert type 'System.Reflection.FieldInfo[]' to 'System.Reflection.FieldInfo'
public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
{
while (objectType != null)
{
//GetFields(...) returns a FieldInfo []
yield return objectType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
objectType = objectType.Ba开发者_如何学编程seType;
}
}
Type.GetFields returns an array of FieldInfo objects.
Since you´re not returning a collection of arrays, you have to iterate over the array and yield return each object in it, kinda like this:
foreach (var fi in objectType.GetFields(...))
yield return fi;
yield return
operates on single elements; you can simply return the return value from GetFields
to achieve what you want.
To explain further, yield return
is mainly useful when you want to apply some operation to each item in a sequence before making it available to the caller, but you don't want to do all of that work up-front and simply return the aggregate results in an array or List<T>
.
yield return
makes this deferred execution easy to write, because the generated iterator block only applies any such operation to each item as it's requested.
EDIT: Sorry - I missed one of the points of your code (enumerating the base fields), but I'll leave this answer up since I think it might be useful anyway. Lasse Karlsen's answer will do what you need, but unless you have a huge type hierarchy, you're not really gaining any advantage over just aggregating the results and returning them in an array.
You can try recursion + Concat
:
public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
{
if (objectType == null)
return Enumerable<FieldInfo>.Empty;
else
return objectType.GetFields(
BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Concat(GetAllFields(objectType.BaseType));
}
What you're looking for is a "yield forall." You'd have to foreach through and yield return each item returned from GetFields and then set to the base class.
This shouldn't be necessary though because there is a flag for whether or not to include base-class definitions.
How about a little recursion?
public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
{
//GetFields(...) returns a FieldInfo []
var fields = objectType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
if(objectType.BaseType==null) return fields;
return fields.Concat(GetAllFields(object.BaseType));
}
As everybody pointed out, the issue is that the type of the element yielded doesn't match with the type expected.
To avoid writing a for/while/foreach every time, I like to use an extension method that handles the iteration for me.
public static class Sequence
{
public static IEnumerable<T> Create<T>(T seed, Func<T, bool> predicate, Func<T, T> next)
{
for (T t = seed; predicate(t); t = next(t))
yield return t;
}
}
That way, you can return the fields writing a much more readable query
public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
{
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
return from type in Sequence.Create(objectType, t => t != null, t => t.BaseType)
from field in type.GetFields(flags)
select field;
}
精彩评论