Wie erhalte ich den Array-Elementtyp vom Array-Typ in .net?


71

Angenommen, ich habe ein System.String[]Typobjekt. Ich kann das Typobjekt abfragen, um festzustellen, ob es sich um ein Array handelt

Type t1 = typeof(System.String[]);
bool isAnArray = t1.IsArray; // should be true

Wie erhalte ich jedoch ein Typobjekt des Array-Elements von t1?

Type t2 = ....; // should be typeof(System.String)

Antworten:


115

Zu Type.GetElementTypediesem Zweck können Sie die Instanzmethode verwenden.

Type t2 = t1.GetElementType();

[Gibt] den Typ des Objekts zurück, das vom aktuellen Array, Zeiger oder Referenztyp umfasst oder auf den verwiesen wird, oder null, wenn der aktuelle Typ kein Array oder Zeiger ist oder nicht als Referenz übergeben wird oder einen generischen Typ darstellt oder Ein Typparameter in der Definition eines generischen Typs oder einer generischen Methode.


Chrrrrrrrrrrrrrrrrrrrr Bro!. Tausend Dank.
Preet Sangha

8
Dies funktioniert für Arrays wie ursprünglich in Frage gestellt. Als Referenz kann auf Sammlungen, die Typ enthalten, als Typ zugegriffen werden. GetGenericArguments () [0]
psaxton

In den Anmerkungen in Ihrem Link stand "Diese Methode wird nullfür die ArrayKlasse zurückgegeben." Es kann also nur bei T[]Formularen funktionieren . Aber es scheint in Ordnung in meinem Test auf einem Arrayvon Array.CreateInstance()...
Mr. Ree

13

Dank des Kommentars von @psaxton , der auf den Unterschied zwischen Array und anderen Sammlungen hinweist. Als Erweiterungsmethode:

public static class TypeHelperExtensions
{
    /// <summary>
    /// If the given <paramref name="type"/> is an array or some other collection
    /// comprised of 0 or more instances of a "subtype", get that type
    /// </summary>
    /// <param name="type">the source type</param>
    /// <returns></returns>
    public static Type GetEnumeratedType(this Type type)
    {
        // provided by Array
        var elType = type.GetElementType();
        if (null != elType) return elType;

        // otherwise provided by collection
        var elTypes = type.GetGenericArguments();
        if (elTypes.Length > 0) return elTypes[0];

        // otherwise is not an 'enumerated' type
        return null;
    }
}

Verwendung:

typeof(Foo).GetEnumeratedType(); // null
typeof(Foo[]).GetEnumeratedType(); // Foo
typeof(List<Foo>).GetEnumeratedType(); // Foo
typeof(ICollection<Foo>).GetEnumeratedType(); // Foo
typeof(IEnumerable<Foo>).GetEnumeratedType(); // Foo

// some other oddities
typeof(HashSet<Foo>).GetEnumeratedType(); // Foo
typeof(Queue<Foo>).GetEnumeratedType(); // Foo
typeof(Stack<Foo>).GetEnumeratedType(); // Foo
typeof(Dictionary<int, Foo>).GetEnumeratedType(); // int
typeof(Dictionary<Foo, int>).GetEnumeratedType(); // Foo, seems to work against key

2
Ich sehe hier ein kleines Problem, was ist mit Klassen mit Generika, die nicht Colleciton oder Array sind. Ich habe if (type.IsArray || type.FullName.StartsWith ("System.Collections")) zur Gleichung hinzugefügt.
André

@ André was ist ein Beispiel? Meinst du eine benutzerdefinierte Klasse? denn wirklich, wenn es ein aufgezählter Typ ist, sollte er (?) von erben IEnumerable; Vielleicht hätte meine Verwendung von "Sammlungen" dann "aufzählbar" sein sollen?
Drzaus

@ André vergleicht nicht die Zeichenfolgen von Typnamen, sondern prüft, ob typeof(IEnumerable).IsAssinableFrom(type).
Shimmy Weitzhandler

@drzaus er meinte was, wenn wir einen generischen Typ überprüfen, der nicht unbedingt IEnumerablezum Beispiel ein IObservable<T>oder was auch immer ist. Ich würde den Methodentyp in ändern GetGenericType.
Shimmy Weitzhandler

2

Vielen Dank an @drzaus für seine nette Antwort , die jedoch zu einem Oneliner komprimiert werden kann (plus Überprüfung auf nulls und IEnumerableTyp):

public static Type GetEnumeratedType(this Type type) =>
   type?.GetElementType()
   ?? typeof(IEnumerable).IsAssignableFrom(type)
   ? type.GenericTypeArguments.FirstOrDefault()
   : null;

Es wurden nullPrüfer hinzugefügt , um Ausnahmen zu vermeiden, vielleicht sollte ich das nicht (zögern Sie nicht, die bedingten Nulloperatoren zu entfernen ). Außerdem wurde ein Filter hinzugefügt, damit die Funktion nur für Sammlungen funktioniert, nicht für generische Typen.

Und denken Sie daran, dass dies auch durch implementierte Unterklassen getäuscht werden könnte, die das Thema der Sammlung ändern, und der Implementierer beschlossen hat, das generische Argument der Sammlung an eine spätere Position zu verschieben.


Konvertierte Antwort für C # 8 und Nullbarkeit:

public static Type GetEnumeratedType(this Type type) => 
        ((type?.GetElementType() ?? (typeof(IEnumerable).IsAssignableFrom(type)
            ? type.GenericTypeArguments.FirstOrDefault()
            : null))!;

Etwas hat sich geändert. Wenn ich diesen Code wörtlich kopiere, wird folgende Fehlermeldung angezeigt: "Operator '??' kann nicht auf Operanden vom Typ 'Type' und 'bool' angewendet werden ". (.Net Core C # 8)
ΩmegaMan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.