2012-10-04 12 views
8

Eventuali duplicati:
getting type T from IEnumerable<T>Come ottenere un tipo di base di un IEnumerable

Ho un tipo di proprietà di IEnumerable

public IEnumerable PossibleValues { get; set; }

Come posso scoprire il tipo di base che è stato istanziato?

Per esempio, se è stato creato in questo modo:

PossibleValues = new int?[] { 1, 2 } 

voglio sapere che tipo è 'int'.

+5

@Arran, che si riferisce alla versione generica –

+0

@Arran: Non necessariamente, se c'è qualsiasi significato sotto l'aspetto che l'OP mostra un array di 'Nullable ', ma vuole recuperare 'int'. –

+0

Ah vero, ignorami :) – Arran

risposta

3

È possibile farlo se si desidera che il tipo di PossibleValues:

var type = PossibleValues.GetType().ToString(); // "System.Nullable`1[System.Int32][]" 

Oppure si può fare questo se si desidera che il tipo di un elemento contenuto in PossibleValues ​​(supponendo che l'array è in realtà i valori come descritto nella domanda):

var type = PossibleValues.Cast<object>().First().GetType().ToString(); // "System.Int32" 



EDIT

Se si tratta di una possibilità che l'array può contenere articoli, quindi dovrete fare qualche controllo nulla, naturalmente:

var firstItem = PossibleValues.Cast<object>().FirstOrDefault(o => o != null); 
var type = string.Empty; 
if (firstItem != null) 
{ 
    type = firstItem.GetType().ToString(); 
} 
+0

Dovrai controllare che la sequenza abbia dei valori prima di tirare il primo. – Servy

+1

Ecco perché ho affermato "presumendo che l'array abbia effettivamente dei valori". – bugged87

+0

Se non ha valori, probabilmente non dovrebbe venire con un tipo (che non funziona ancora) ma non lanciare un'eccezione. – Servy

8
Type GetBaseTypeOfEnumerable(IEnumerable enumerable) 
{ 
    if (enumerable == null) 
    { 
     //you'll have to decide what to do in this case 
    } 

    var genericEnumerableInterface = enumerable 
     .GetType() 
     .GetInterfaces() 
     .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); 

    if (genericEnumerableInterface == null) 
    { 
     //If we're in this block, the type implements IEnumerable, but not IEnumerable<T>; 
     //you'll have to decide what to do here. 

     //Justin Harvey's (now deleted) answer suggested enumerating the 
     //enumerable and examining the type of its elements; this 
     //is a good idea, but keep in mind that you might have a 
     //mixed collection. 
    } 

    var elementType = genericEnumerableInterface.GetGenericArguments()[0]; 
    return elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(Nullable<>) 
     ? elementType.GetGenericArguments()[0] 
     : elementType; 
} 

Questo esempio ha alcune limitazioni, che può o non può interessarti nella tua domanda. Non gestisce il caso in cui il tipo implementa IEnumerable ma non IEnumerable<T>. Se il tipo implementa IEnumerable<T> più di una volta, seleziona un'implementazione arbitrariamente.

+0

Grazie, quando avrò abbastanza reputazione, rispondo alla tua risposta. –

+0

Sto usando un'altra risposta, ma l'apprendimento è stato buono. –

+0

@RaoniZacaron Ho messo in discussione la tua domanda, quindi ora hai abbastanza reputazione per alzare le risposte (congratulazioni). Non dimenticare di accettare la risposta che stai utilizzando! – phoog

2

Due approcci esistenti sono per vedere se l'oggetto implementa IEnumerable<T> o per verificare il tipo del primo elemento nel set. Il primo si basa sull'oggetto che attualmente implementa IEnumerable<T> e il secondo funziona solo se tutti gli elementi nella sequenza sono dello stesso tipo derivato.

Una domanda interessante che si potrebbe chiedere è quali tipi hanno tutti gli elementi in comune o qual è il tipo più stretto comune a tutti gli elementi.

Iniziamo con un semplice metodo di supporto. Dato un tipo restituirà una sequenza di quel tipo e tutti i suoi tipi di base.

public static IEnumerable<Type> getBaseTypes(Type type) 
{ 
    yield return type; 

    Type baseType = type.BaseType; 
    while (baseType != null) 
    { 
     yield return baseType; 
     baseType = baseType.BaseType; 
    } 
} 

successivo avremo un metodo per ottenere tutti i tipi comuni di una sequenza dalla prima trovare tutti i tipi più derivati, ottenendo quindi tutti i tipi di base per ogni tipo nella sequenza, e, infine, utilizzando intersect per ottenere solo gli elementi che tutti hanno in comune:

public static IEnumerable<Type> getCommonTypes(IEnumerable source) 
{ 
    HashSet<Type> types = new HashSet<Type>(); 
    foreach (object item in source) 
    { 
     types.Add(item.GetType()); 
    } 

    return types.Select(t => getBaseTypes(t)) 
     .Aggregate((a, b) => a.Intersect(b)); 
} 

si noti che l'ordinamento dei tipi nel primo metodo è dalla maggior parte deriva da almeno derivati, e Intersect mantiene ordinamento, in modo che la sequenza risultante sarà in ordine dalla maggior parte derivata a meno derivata. Se si desidera trovare il tipo più stretto comune a tutti questi tipi, è sufficiente utilizzare First sul risultato di questo metodo. (Si noti che poiché tutto deriva da object ci sarà sempre almeno un tipo restituito qui a meno che non ci siano articoli nell'originale IEnumerable.

Problemi correlati