2010-02-28 10 views
6

he escrito este método de extensión:Cómo llamar a un método de extensión genérico de forma dinámica?

public static DataTable ToDataTable<T>(this IList<T> list) 
{...} 

Funciona bien si se llama con un tipo conocido en tiempo de compilación:

DataTable tbl = new List<int>().ToDataTable(); 

Pero, ¿cómo llamarlo si el tipo genérico no se conoce?

object list = new List<int>(); 
... 
tbl = Extension.ToDataTable((List<object>)list); // won't work 
+0

¿Por qué echaron a '' Lista ? Su 'lista' es una' Lista ', el elenco no tendrá éxito. – Vlad

+0

Porque no sabe en tiempo de compilación qué tipo de lista tiene: no sabe que es 'List '. Está tratando de sortearlo lanzando a una clase base (que, como bien anotará, no funcionará porque 'List ' no es compatible con 'List ' aunque 'int' sea compatible con' object') . – itowlson

Respuesta

9

Esto se produce porque un List<int> no es un List<object> - el tipo de lista no es covariante en su parámetro de tipo de elemento. Desafortunadamente usted necesita para obtener una versión mecanografiada del método genérico y lo llaman utilizando la reflexión:

Type listItemType = typeof(int); // cheating for simplicity - see below for real approach 
MethodInfo openMethod = typeof(Extension).GetMethod("ToDataTable", ...); 
MethodInfo typedMethod = openMethod.MakeGenericMethod(typeof(listItemType)); 
typedMethod.Invoke(null, new object[] { list }); 

Una alternativa puede ser la creación de una versión de su método de extensión que acepte IList en lugar de IList<T>. La clase List<T> implementa esta interfaz no genérico, así como la interfaz genérica, por lo que será capaz de llamar a:

public static DataTable WeakToDataTable(this IList list) { ... } 

((IList)list).WeakToDataTable(); 

(En realidad lo que probablemente utilice una sobrecarga en lugar de un nombre diferente - sólo usar un nombre diferente para llamar a los diferentes tipos)


Más información:. En la solución de reflexión, me he saltado sobre el problema de cómo determinar el tipo de lista de elementos. Esto puede ser un poco complicado según lo sofisticado que desee obtener. Si usted está asumiendo que el objeto será una List<T> (para algunos T) entonces es fácil:

Type listItemType = list.GetType().GetGenericArguments()[0]; 

Si sólo estás dispuesto a asumir IList<T> entonces es un poco más difícil, porque hay que localizar el apropiado interfaz y obtener el argumento genérico de eso. Y no puede usar GetInterface() porque está buscando una instancia cerrada y construida de una interfaz genérica. Así que hay que arrastrarse a través de todas las interfaces en busca de uno que es una instancia de IList<T>:

foreach (Type itf in list.GetType().GetInterfaces()) 
{ 
    if (itf.IsGenericType && itf.GetGenericTypeDefinition == typeof(IList<>)) // note generic type definition syntax 
    { 
    listItemType = itf.GetGenericArguments()[0]; 
    } 
} 

Esto funcionará para las listas vacías porque sale de los metadatos, no el contenido de la lista.

+0

¿No solo se lanzará a 'List ' en lugar de 'List ' resolver el problema? – Vlad

+0

Claro, pero la pregunta pregunta "¿cómo llamarlo ** si no se conoce el tipo genérico **?" (énfasis añadido).De ahí mi nota de que, en realidad, también tendría que descubrir la lista ItemType utilizando la reflexión en lugar de simplemente suponer que es int. – itowlson

+0

Intenté esto, pero tengo dos problemas: 1. ¿Cómo obtendré el tipo de elementos incrustados si la lista está vacía? 2. Tengo dos métodos de extensión ToDataTable(). ¿Cómo obtener el de IList ? –

0

Después de tener problemas para hacerlo funcionar con la interfaz IList<T> lo resolví utilizando la interfaz IList como itowlson propuesta. Es un poco feo debido al método _T pero funciona bien:

DataTable tbl = ((IList)value).ToDataTable(); 

public static class Extensions 
{ 
    private static DataTable ToDataTable(Array array) {...} 
    private static DataTable ToDataTable(ArrayList list) {...} 
    private static DataTable ToDataTable_T(IList list) {...} 

    public static DataTable ToDataTable(this IList list) 
    { 
     if (list.GetType().IsArray) 
     { 
      // handle arrays - int[], double[,] etc. 
      return ToDataTable((Array)list); 
     } 
     else if (list.GetType().IsGenericType) 
     { 
      // handle generic lists - List<T> etc. 
      return ToDataTable_T(list); 
     } 
     else 
     { 
      // handle non generic lists - ArrayList etc. 
      return ToDataTable((ArrayList)list); 
     }    
    } 
} 
Cuestiones relacionadas