2011-06-13 16 views
5

Esto es un poco difícil de explicar. Entonces aquí va.¿Cómo puedo enumerar a través de un objeto de tipo `T` cuando es un IEnumerable

que tienen una función como esta:

public T FooBar<T>(Func<T> function) 
{ 
    T returnData = function(); 

    // want to iterate through returnData to do something to it 

    return returnData; 
} 

Si el returnData (t) es una lista IEnumerable, a continuación, me gustaría enumerar returnData modificar su contenido utilizando la reflexión. Pero parece que no puedo hacerlo. Cuando trato de echar returnData a un tipo enumerable, consigo una excepción:

No se puede convertir objeto de tipo

'System.Collections.Generic.List`1 [Coches]'

para escribir

'System.Collections.Generic.List`1 [System.Object]'.

No sabré que el tipo de devolución será una lista de 'automóviles', por ejemplo, con anticipación, solo en tiempo de ejecución. Así que tengo que verificar utilizando el reflejo si se trata de una lista, y luego tratar de lanzarlo para que pueda enumerar a través de él.

A menos que lo esté haciendo de la manera incorrecta. ¿Cómo puedo enumerar a través del returnData si es del tipo T?

Respuesta

4
if (returnData is System.Collections.IEnumerable) 
{ 
    foreach (object o in (System.Collections.IEnumerable)returnData) 
    { 
     // Do something. 
    } 
} 

Realmente, sin embargo, por qué no tener una sobrecarga adicional de la siguiente manera:

public T FooBar<T>(Func<IEnumerable<T>> function) 
+0

No he pensado en la idea de la sobrecarga. Voy a intentar esto. – 7wp

6

Un método consiste en agregar una restricción de tipo de T, pero esto no es lo ideal:

public T FooBar<T>(Func<T> function) where T : IEnumerable 
{ 
    // T is not strongly typed for the enumerated item 

Si cambió su método ligeramente (wrt T):

public IEnumerable<T> FooBar<T>(Func<IEnumerable<T>> function) 

Luego tiene una fuerte tipa en el elemento real que se enumera con la ventaja adicional de aceptar objetos enumerables.


me he dado cuenta de una segunda lectura de su pregunta, hay cierta confusión acerca de lo que significa para su T variables returnData. En el caso donde se pasa FooBar(), List<Car>, T es List<Car>, y realmente no tiene asociación con la especificación de tipo genérico del List<>. Puede considerarlo como un List<U> donde U es otro tipo desconocido.

En tiempo de ejecución no tendrá una forma simple de obtener U ya que está oculto, por así decirlo, dentro de T. Puede utilizar la sobrecarga como lo recomiendan algunos de los otros contestadores, y proporcionar un método que no sea IEnumerable<U> y uno que tome argumentos del tipo Func<IEnumerable<T>>.

Tal vez con más detalles sobre el objetivo de FooBar<T> podríamos hacer algunas recomendaciones más específicas.

2

¿Ha probado el tipo de fundición a IEnumerable en lugar de IEnumerable<T>? Con IEnumerable, puede usarlo en un bucle foreach. La variable de cada elemento iría en debe ser de tipo object es decir .:

foreach(object item in (IEnumerable)T){...} 

Usted debe verificar primero para asegurarse de que T implementa IEnumerable.

1

El problema aquí es IEnumerable e IEnumerable Of T no son lo mismo ... pero puede verificar la diferencia y dar cuenta de ello en su código. Tenga en cuenta que IEnumerable Of T hereda IEnumerable, por lo que puede ajustar la verificación de la versión genérica dentro de la versión no genérica.

Lo siguiente funcionó para mí en una pequeña prueba que escribí - Espero que sea suficiente para que usted haga lo que necesita.

Aquí está la carne y patatas:

class FooBarOfT 
{ 
    public T FooBar<T>(Func<T> function) 
    { 
     T returnData = function(); 

     //Want to iterate through returnData to do something to it. 
     if (returnData is IEnumerable) 
     { 
      // get generic type argument 
      var returnDataType = returnData.GetType(); 

      if (returnDataType.IsGenericType) 
      { 
       // this is a System.Collections.Generic.IEnumerable<T> -- get the generic type argument to loop through it 
       Type genericArgument = returnDataType.GetGenericArguments()[0]; 

       var genericEnumerator = 
        typeof(System.Collections.Generic.IEnumerable<>) 
         .MakeGenericType(genericArgument) 
         .GetMethod("GetEnumerator") 
         .Invoke(returnData, null); 

       IEnumerator enm = genericEnumerator as IEnumerator; 
       while (enm.MoveNext()) 
       { 
        var item = enm.Current; 
        Console.WriteLine(string.Format("Type : {0}", item.GetType().Name)); 
       } 

      } 
      else 
      { 
       // this is an System.Collections.IEnumerable (not generic) 
       foreach (var obj in (returnData as IEnumerable)) 
       { 
        // do something with your object 
       } 
      } 
     } 

     return returnData; 
    } 
} 

también establecer algunas clases de prueba de apoyo:

class Foo 
{ 
    private string _fooText; 

    public Foo(string fooText) 
    { 
     _fooText = fooText; 
    } 
    public string Execute() 
    { 
     return string.Format("executed! with {0} !", _fooText); 
    } 
} 

class Bar 
{ 
    public string BarContent { get; set; } 
} 

y una pequeña aplicación de consola para ejecutar algunas pruebas:

class Program 
{ 
    static void Main(string[] args) 
    { 
     // tests 
     Func<string> stringFunc =() => 
      "hello!"; 

     Func<List<Foo>> listFooFunc =() => 
      new List<Foo> 
      { 
       new Foo("Hello!"), 
       new Foo("World!") 
      }; 

     Func<IEnumerable> ienumerableFooFunc =() => 
      new Hashtable 
      { 
       { "ItemOne", "Foo" }, 
       { "ItemTwo", "Bar" } 
      }; 


     var fooBarOfT = new FooBarOfT(); 

     fooBarOfT.FooBar(stringFunc); 
     fooBarOfT.FooBar(listFooFunc); 
     fooBarOfT.FooBar(ienumerableFooFunc); 

     Console.ReadKey(); 
    } 
} 
Cuestiones relacionadas