2010-12-30 6 views
19

Estoy intentando crear una implementación para NotOfType, que tiene una sintaxis de llamada legible. NotOfType debe ser el complemento a OfType<T> y sería por lo tanto producir todos los elementos que son no de tipo T¿Cómo puedo implementar NotOfType <T> en LINQ que tiene una buena sintaxis de llamadas?

Mi objetivo era poner en práctica un método que se llama igual que OfType<T>, al igual que en la última línea de este fragmento:

public abstract class Animal {} 
public class Monkey : Animal {} 
public class Giraffe : Animal {} 
public class Lion : Animal {} 

var monkey = new Monkey(); 
var giraffe = new Giraffe(); 
var lion = new Lion(); 

IEnumerable<Animal> animals = new Animal[] { monkey, giraffe, lion }; 

IEnumerable<Animal> fewerAnimals = animals.NotOfType<Giraffe>(); 

Sin embargo, no puedo encontrar una implementación que admita esa sintaxis de llamada específica.

Esto es lo que he probado hasta ahora:

public static class EnumerableExtensions 
{ 
    public static IEnumerable<T> NotOfType<T>(this IEnumerable<T> sequence, Type type) 
    { 
     return sequence.Where(x => x.GetType() != type); 
    } 

    public static IEnumerable<T> NotOfType<T, TExclude>(this IEnumerable<T> sequence) 
    { 
     return sequence.Where(x => !(x is TExclude)); 
    } 
} 

llamar a estos métodos se vería así:

// Animal is inferred 
IEnumerable<Animal> fewerAnimals = animals.NotOfType(typeof(Giraffe)); 

y

// Not all types could be inferred, so I have to state all types explicitly 
IEnumerable<Animal> fewerAnimals = animals.NotOfType<Animal, Giraffe>(); 

Creo que hay grandes inconvenientes con el estilo de ambas llamadas. El primero sufre de una construcción redundante de "tipo/tipo de", y el segundo simplemente no tiene sentido (¿quiero una lista de animales que no son ni animales ni jirafas?).

Entonces, ¿hay alguna manera de lograr lo que quiero? Si no, ¿podría ser posible en futuras versiones del lenguaje? (Estoy pensando que tal vez algún día tengamos argumentos de tipo con nombre, o que solo necesitemos proporcionar explícitamente argumentos de tipo que no se puedan inferir?)

¿O solo estoy siendo tonto?

+0

sólo escribir marco/código compartido como éste si siento que va a obtener una buena cantidad de reutilización. ¿Con qué frecuencia necesita un método como este y justifica el costo (tiempo dedicado a pensar en las dificultades de diseño)? –

+1

Sus dos versiones hacen cosas sustancialmente diferentes. – SLaks

+0

@SLaks: Elaborar, por favor. –

Respuesta

10

¿Qué tal

animals.NotOf(typeof(Giraffe)); 

Alternativamente, you can split the generic parameters across two methods:

animals.NotOf().Type<Giraffe>(); 

public static NotOfHolder<TSource> NotOf<TSource>(this IEnumerable<TSource> source); 

public class NotOfHolder<TSource> : IHideObjectMembers { 
    public IEnumerable<TSource> NotOf<TNot>(); 
} 

Además, es necesario decidir si también se excluye tipos heredados.

+0

Ah. Este enfoque "fluido" parece una buena alternativa. –

+0

Escribí una publicación de blog que explica con más detalle: http://blog.slaks.net/2010/12/partial-type-inference-in-net.html – SLaks

+0

Finalmente, he llegado a la conclusión de que su solución será la mejor para mi escenario específico. Estoy teniendo en cuenta el hecho de que el código se utilizará en pruebas de aceptación automáticas, y es posible que en algún momento lo lean personas que quizás no sean desarrolladores a tiempo completo. Tener una llamada fuertemente tipada hará que sea más fácil mantener el código posterior más legible (humano). Gracias. –

5

Esto podría parecer una sugerencia extraña, pero ¿qué pasa con un método de extensión en el antiguo IEnumerable? Esto reflejaría la firma de OfType<T>, y también eliminaría el problema de los parámetros de tipo redundante <T, TExclude>.

También argumentaría que si ya tiene una secuencia fuertemente tipada, hay muy pocas razones para un método especial NotOfType<T>; parece mucho más útil (en mi opinión) excluir un tipo específico de una secuencia de tipo arbitrario ... o déjenme decirlo de esta manera: si se trata de un IEnumerable<T>, es trivial llamar al Where(x => !(x is T)) ; la utilidad de un método como NotOfType<T> se vuelve más cuestionable en este caso.

+0

Idea interesante sobre el IEnumerable no genérico. Hará que el código de llamada se vea exactamente como yo quería. Obviamente, no devolverá una secuencia fuertemente tipada, que es un inconveniente menor. –

+0

* Con respecto al segundo párrafo: * Uno * podría * argumentar que la utilidad de 'NotOfType ' coincide con la de 'OfType ' ...(En cambio, es interesante intentar aplicar tus argumentos a 'OfType '). Mi argumento es solo acerca de la legibilidad del código. Para tu información, en el caso real de negocios, tengo una secuencia de elementos de "Evento" que son de diferentes subtipos, y en un escenario específico, los requisitos requerían una lista de todos los eventos, excepto un tipo particular. Este experimento consistió en encontrar la forma de expresar ese requisito en un código de fácil lectura. –

+1

@Lette: 'OfType ()' es diferente porque cambia el tipo de la secuencia. 'NotOfType' no puede cambiar el tipo de la secuencia. – SLaks

25

No estoy seguro de por qué no acaba de decir:

animals.Where(x => !(x is Giraffe)); 

esto parece perfectamente legible para mí.Ciertamente, es más directo para mí que animals.NotOfType<Animal, Giraffe>(), lo que me confundiría si lo descubriera ... el primero nunca me confundiría, ya que es inmediatamente legible.

Si querías una interfaz fluida, supongo que también podría hacer algo como esto con un método predicado extensión en Object:

animals.Where(x => x.NotOfType<Giraffe>()) 
1

Si va a realizar un método para la inferencia, que desea inferir todo el camino. Eso requiere un ejemplo de cada tipo:

public static class ExtMethods 
{ 
    public static IEnumerable<T> NotOfType<T, U>(this IEnumerable<T> source) 
    { 
     return source.Where(t => !(t is U)); 
    } 
     // helper method for type inference by example 
    public static IEnumerable<T> NotOfSameType<T, U>(
     this IEnumerable<T> source, 
     U example) 
    { 
     return source.NotOfType<T, U>(); 
    } 
} 

llamado por

List<ValueType> items = new List<ValueType>() { 1, 1.0m, 1.0 }; 
IEnumerable<ValueType> result = items.NotOfSameType(2); 
0

He intentado esto y funciona ...

public static IEnumerable<TResult> NotOfType<TExclude, TResult>(this IEnumerable<TResult> sequence) 
    => sequence.Where(x => !(x is TExclude)); 

Me estoy perdiendo algo?

+0

No es que no funcione _. Mi objeción es que necesitaría especificar ambos tipos ('TSource',' TResult') para la llamada genérica y eso simplemente 'parece_ raro para esta llamada específica. –

+0

Acepto que se ve raro ... – franklores

0

Considere esta

public static IEnumerable NotOfType<TResult>(this IEnumerable source) 
{ 
    Type type = typeof(Type); 

    foreach (var item in source) 
    { 
     if (type != item.GetType()) 
     { 
      yield return item; 
     } 
    } 
} 
0

tuve un problema similar, y me encontré con esta pregunta, mientras que en busca de una respuesta.

Yo en cambio se conformaron con la siguiente sintaxis llamando:

var fewerAnimals = animals.Except(animals.OfType<Giraffe>()); 

tiene la desventaja de que enumera la colección dos veces (por lo que no se puede utilizar con una serie infinita), pero la ventaja de que ninguna nueva función auxiliar es requerido, y el significado es claro.

En mi caso uso real, también terminó la adición de un .Where(...) después de los .OfType<Giraffe>() (jirafas también incluidos a menos que cumplan una condición particular exclusión que sólo tiene sentido para las jirafas)

Cuestiones relacionadas