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)
Preet Sangha
quelle

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.

Ani
quelle
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
drzaus
quelle
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))!;
Shimmy Weitzhandler
quelle
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