2009-03-04 14 views
5

Estoy escribiendo una función de filtro para devolver el tipo específico especificado de una colección más grande de supertipos (objetos, por ejemplo). La idea es que te dé un enumerable y me devuelvas todos los hilos, por ejemplo. se puede escribir de esta manera sin genéricos:¿Cuál es la forma adecuada de escribir con fuerza el retorno de una función genérica?

public static IEnumerable Filter(IEnumerable source, Type type) 
{ 
    List<object> results = new List<object>(); 

    foreach(object o in source) 
    { 
     if(o != null && o.GetType() == type) 
     { 
      results.Add(o); 
     } 
    } 

    return results; 
} 

si queremos volver genéricos hay algunas maneras diferentes de ir sobre él.

Como puerto recta:

public static IEnumerable<TResult> Filter<TResult> 
          (IEnumerable source, Type type) 

Pass en un 'ejemplo':

IEnumerable<TResult> Filter<TResult> 
    (IEnumerable source, TResult resultType) 

En última instancia lo que creo que es más limpio:

public static IEnumerable<T> Filter<T>(IEnumerable source) 

El segundo tipo se llama completamente con parámetros (e inferir el tipo):

Filter(myList, "exampleString"); 

en tanto que la versión final no se obtendría llamada con un especificador de tipo:

Filter<string>(myList); 

¿Cuál es la forma apropiada de escribir fuertemente el retorno de una función genérica, donde el tipo de retorno no se implica de forma automática en la firma? (¿Por qué?)

(Editar Nota: Nuestra entrada no se escribe, por ejemplo, IEnumerable <T> En el mejor sería IEnumerable Esta función devuelve el Ts de toda la colección de otros tipos...)

Respuesta

9

El siguiente método de extensión incluido en LINQ hace exactamente lo que necesita:

IEnumerable<T> OfType<T>(this IEnumerable enumerable); 

Aquí está un ejemplo de uso:

List<object> objects = //... 

foreach(string str in objects.OfType<string>()) 
{ 
    //... 
} 

Como puede ver, usaron el parámetro genérico como el especificador de tipo de retorno. Esto es más simple y seguro que usar un tipo o una cadena y devolver una enumeración segura que no sea de tipo.

+1

Así que la forma '.net' parece estar especificando el tipo, es bueno saber, gracias. Lamentablemente, estamos en un mundo 2.0 y tenemos suerte de tener genéricos. –

+0

Sí, entiendo, estuvimos estancados con 2.0 por bastante tiempo aquí también. Supongo que lo más cerca que podrías llegar es .Net es tu última solución. Lo único que falta sería el método de extensión. – Coincoin

+0

Marqué esto como aceptado a causa de la fuente citada, buen pensamiento. –

1

La manera más limpia sería

public static IEnumerable<T> Filter<T>(IEnumerable<T> source) 

Esto elimina todas las funciones de seguridad no tipo. A continuación, puede convertir cualquier IEnumerable no genérica en una versión genérica con una llamada elenco

IEnumerable enumerable = GetMyEnumerable(); 
var filtered = Filter(enumerable.Cast<string>()); 

También podría, además, que sea un método de extensión y hacer la llamada a simplificar aún más.

public static IEnumerable<T> Filter<T>(this IEnumerable<T> source) 
... 
var filtered = GetMyEnumerable().Cast<string>().Filter(); 

EDITAR

OP mencionó que sólo quieren filtrar a tipos específicos. En ese caso, puedes usar Enumerable.OfType

var filtered = GetMyEnumerable().OfType<SomeType>(); 
+0

Olvidó la palabra clave 'this', la solucionó por usted. –

+0

@Joel gracias! Incluso lo agregué dos veces específicamente para agregar eso y mostrar la diferencia. – JaredPar

+0

Actualizaré la primera publicación para que quede más claro, pero estamos procesando enumeraciones de tipos mezclados. IEnumerable en el mejor. Estamos filtrando para JUSTAR los Ts en la enumeración. –

4

generalmente prefiero la versión final - que especifica toda la información relevante y nada más. Dada la versión con parámetros, si era nuevo en el código, ¿no esperaría que el valor del parámetro fuera significativo, en lugar de solo el tipo?

Muy ocasionalmente este patrón de "parámetro ficticio" es útil, pero en general me mantengo alejado de él, o al menos proporciono una sobrecarga que no lo requería.

+0

Jon, ¡Gracias por responder la pregunta filosófica verbalmente! Si pudiera marcar dos respuestas como 'aceptada', esta sería la segunda. El otro citó el código MS en LINQ, que es más autorizado, ¡pero también aprecio la discusión! Gracias. –

0

este es mi 2 centavos. Estoy un poco confundido acerca de lo que estás tratando de filtrar. IEnumerable no es genérico, entonces, ¿cómo va a filtrar una fuente no genérica y devolver un resultado de IEnuerable?

creo que el más limpio es

public static IEnumerable<T> Filter<T>(IEnumerable<T> source) 

incluso se puede poner en los controles de tipo genérico, si usted sabe qué clase de tipos de su filtro de T será, por ejemplo, solo clases u objetos de cierta interfaz o clase base, o tipos de valores ... como tal.

public static IEnumerable<T> Filter<T>(IEnumerable<T> source) where T : class 
public static IEnumerable<T> Filter<T>(IEnumerable<T> source) where T : struct 
+0

Actualicé la primera publicación para ser más claro. Estoy sacando las Ts de una colección de objetos. En el mejor de los casos, sería IEnumerable en la entrada. No se puede deducir el retorno de eso, de ahí la pregunta. –

1

Si está utilizando Framework 3.5, esto ya se implementa en IEnumerable:

IEnumerable<string> s = someList.OfType<string>() 
0

Está claro (para mí, al menos) que te gustaría la versión final:

IEnumerable<T> Filter<T>(IEnumerable source) 

por proceso de reducción, si nada más.

La primera versión:

IEnumerable<T> Filter<T>(IEnumerable source, Type type) 

tiene que lidiar con la locura, donde yo paso en un tipo que no coincide con la restricción:

La segunda versión:

IEnumerable<T> Filter<T>(IEnumerable source, T type) 

me hace construye un ejemplo, que puede ser costoso o no estar disponible para la construcción. Además, ¿qué pasa si paso nulo (en cualquier caso)?

Como acotación al margen, por las limitaciones individuales, creo que el parámetro de tipo debe ser T. Puede salirse con TResult si es el retorno de una función - como Func <TResult>, pero por lo demás es sólo teclear más con menos claridad .

0

Creo que esto es más o menos lo que quieres.

public static IEnumerable<T> OfType<T>(IEnumerable source) { 
    foreach (object obj in source) 
     if (obj is T) 
      yield return (T)obj; 
} 

Una versión un poco más complejo, pero (probablemente) un poco más rápido sería

public static IEnumerable<T> OfType<T>(IEnumerable source) { 
    foreach (object obj in source) { 
     T result = obj as T; 
     if (result != null) 
      yield return result; 
    } 
} 
+0

Use y guarde el molde duplicado: http://msdn.microsoft.com/en-us/library/cscsdfbt(VS.71).aspx –

Cuestiones relacionadas