gibt es eine Möglichkeit Art abzurufen T
aus IEnumerable<T>
durch Reflexion?
z.B
Ich habe eine variable IEnumerable<Child>
Info. Ich möchte den Typ des Kindes durch Reflexion abrufen
gibt es eine Möglichkeit Art abzurufen T
aus IEnumerable<T>
durch Reflexion?
z.B
Ich habe eine variable IEnumerable<Child>
Info. Ich möchte den Typ des Kindes durch Reflexion abrufen
Antworten:
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
Somit ist
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
druckt System.String
.
Siehe MSDN für Type.GetGenericArguments
.
Bearbeiten: Ich glaube, dies wird die Bedenken in den Kommentaren ansprechen:
// returns an enumeration of T where o : IEnumerable<T>
public IEnumerable<Type> GetGenericIEnumerables(object o) {
return o.GetType()
.GetInterfaces()
.Where(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0]);
}
Einige Objekte implementieren mehr als ein generisches Objekt, IEnumerable
daher muss eine Aufzählung von ihnen zurückgegeben werden.
Bearbeiten: Obwohl, ich muss sagen, es eine schreckliche Idee für eine Klasse ist, IEnumerable<T>
für mehr als eine zu implementieren T
.
Ich würde nur eine Erweiterungsmethode machen. Dies funktionierte mit allem, was ich darauf warf.
public static Type GetItemType<T>(this IEnumerable<T> enumerable)
{
return typeof(T);
}
Ich hatte ein ähnliches Problem. Die ausgewählte Antwort funktioniert für tatsächliche Instanzen. In meinem Fall hatte ich nur einen Typ (von a PropertyInfo
).
Die ausgewählte Antwort schlägt fehl, wenn der Typ selbst typeof(IEnumerable<T>)
keine Implementierung von ist IEnumerable<T>
.
In diesem Fall funktioniert Folgendes:
public static Type GetAnyElementType(Type type)
{
// Type is Array
// short-circuit if you expect lots of arrays
if (type.IsArray)
return type.GetElementType();
// type is IEnumerable<T>;
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof (IEnumerable<>))
return type.GetGenericArguments()[0];
// type implements/extends IEnumerable<T>;
var enumType = type.GetInterfaces()
.Where(t => t.IsGenericType &&
t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GenericTypeArguments[0]).FirstOrDefault();
return enumType ?? type;
}
Type.GenericTypeArguments
- nur für dotNet FrameWork Version> = 4.5. Ansonsten - Type.GetGenericArguments
stattdessen verwenden.
Wenn Sie das kennen IEnumerable<T>
(über Generika), dann typeof(T)
sollte es einfach funktionieren. Andernfalls (für object
oder nicht generisch IEnumerable
) überprüfen Sie die implementierten Schnittstellen:
object obj = new string[] { "abc", "def" };
Type type = null;
foreach (Type iType in obj.GetType().GetInterfaces())
{
if (iType.IsGenericType && iType.GetGenericTypeDefinition()
== typeof(IEnumerable<>))
{
type = iType.GetGenericArguments()[0];
break;
}
}
if (type != null) Console.WriteLine(type);
Type type
Parameter und nicht als object obj
Parameter: Sie können nicht nur ersetzen obj.GetType()
mit , type
denn wenn man in geben typeof(IEnumerable<T>)
Sie bekommen nichts. Um dies zu umgehen, testen Sie das type
selbst, um festzustellen, ob es sich um ein Generikum von IEnumerable<>
und dann um seine Schnittstellen handelt.
Vielen Dank für die Diskussion. Ich habe es als Grundlage für die unten stehende Lösung verwendet, die für alle Fälle, die für mich von Interesse sind (IEnumerable, abgeleitete Klassen usw.), gut funktioniert. Ich dachte, ich sollte hier teilen, falls jemand es auch braucht:
Type GetItemType(object someCollection)
{
var type = someCollection.GetType();
var ienum = type.GetInterface(typeof(IEnumerable<>).Name);
return ienum != null
? ienum.GetGenericArguments()[0]
: null;
}
someCollection.GetType().GetInterface(typeof(IEnumerable<>).Name)?.GetGenericArguments()?.FirstOrDefault()
Eine Alternative für einfachere Situationen, in denen entweder eine IEnumerable<T>
oder eine T
Notiz GenericTypeArguments
anstelle von verwendet wird GetGenericArguments()
.
Type inputType = o.GetType();
Type genericType;
if ((inputType.Name.StartsWith("IEnumerable"))
&& ((genericType = inputType.GenericTypeArguments.FirstOrDefault()) != null)) {
return genericType;
} else {
return inputType;
}
Dies ist eine Verbesserung der Lösung von Eli Algranti, da sie auch dort funktioniert, wo sich der IEnumerable<>
Typ auf einer beliebigen Ebene im Vererbungsbaum befindet.
Diese Lösung erhält den Elementtyp von jedem Type
. Wenn der Typ kein ist IEnumerable<>
, wird der übergebene Typ zurückgegeben. Verwenden Sie für Objekte GetType
. Verwenden Sie für Typen typeof
diese Erweiterungsmethode und rufen Sie sie für das Ergebnis auf.
public static Type GetGenericElementType(this Type type)
{
// Short-circuit for Array types
if (typeof(Array).IsAssignableFrom(type))
{
return type.GetElementType();
}
while (true)
{
// Type is IEnumerable<T>
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
return type.GetGenericArguments().First();
}
// Type implements/extends IEnumerable<T>
Type elementType = (from subType in type.GetInterfaces()
let retType = subType.GetGenericElementType()
where retType != subType
select retType).FirstOrDefault();
if (elementType != null)
{
return elementType;
}
if (type.BaseType == null)
{
return type;
}
type = type.BaseType;
}
}
Ich weiß, dass dies ein bisschen alt ist, aber ich glaube, dass diese Methode alle in den Kommentaren genannten Probleme und Herausforderungen abdecken wird. Dank an Eli Algranti für die Inspiration meiner Arbeit.
/// <summary>Finds the type of the element of a type. Returns null if this type does not enumerate.</summary>
/// <param name="type">The type to check.</param>
/// <returns>The element type, if found; otherwise, <see langword="null"/>.</returns>
public static Type FindElementType(this Type type)
{
if (type.IsArray)
return type.GetElementType();
// type is IEnumerable<T>;
if (ImplIEnumT(type))
return type.GetGenericArguments().First();
// type implements/extends IEnumerable<T>;
var enumType = type.GetInterfaces().Where(ImplIEnumT).Select(t => t.GetGenericArguments().First()).FirstOrDefault();
if (enumType != null)
return enumType;
// type is IEnumerable
if (IsIEnum(type) || type.GetInterfaces().Any(IsIEnum))
return typeof(object);
return null;
bool IsIEnum(Type t) => t == typeof(System.Collections.IEnumerable);
bool ImplIEnumT(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
public static Type GetInnerGenericType(this Type type)
{
// Attempt to get the inner generic type
Type innerType = type.GetGenericArguments().FirstOrDefault();
// Recursively call this function until no inner type is found
return innerType is null ? type : innerType.GetInnerGenericType();
}
Dies ist eine rekursive Funktion, die zuerst die Liste der generischen Typen vertieft, bis eine konkrete Typdefinition ohne innere generische Typen erhalten wird.
Ich habe diese Methode mit folgendem Typ getestet:
ICollection<IEnumerable<ICollection<ICollection<IEnumerable<IList<ICollection<IEnumerable<IActionResult>>>>>>>>
was zurückkehren sollte IActionResult
typeof(IEnumerable<Foo>)
. gibt das erste generische Argument zurück - in diesem Fall .GetGenericArguments()
[0]
typeof(Foo)
Hier ist meine unlesbare Version des Linq-Abfrageausdrucks.
public static Type GetEnumerableType(this Type t) {
return !typeof(IEnumerable).IsAssignableFrom(t) ? null : (
from it in (new[] { t }).Concat(t.GetInterfaces())
where it.IsGenericType
where typeof(IEnumerable<>)==it.GetGenericTypeDefinition()
from x in it.GetGenericArguments() // x represents the unknown
let b = it.IsConstructedGenericType // b stand for boolean
select b ? x : x.BaseType).FirstOrDefault()??typeof(object);
}
Beachten Sie, dass die Methode auch nicht generische IEnumerable
Elemente berücksichtigt. object
In diesem Fall wird sie zurückgegeben, da aType
als Argument eher eine als eine konkrete Instanz verwendet wird. Übrigens, da x das Unbekannte darstellt , fand ich dieses Video interessant, obwohl es irrelevant ist.