2011-11-23 14 views
5

Básicamente tengo una lista de objetos donde cada objeto podría implementar un conjunto diferente de las interfaces:lista de filtrado usando múltiples clases/interfaces

List<BaseObject> objects; 

class BaseObject 
{ 
    public void DoStuff(); 
} 

interface IX 
{ 
    void DoX(); 
} 

interface IY 
{ 
    void DoY(); 
} 

interface IZ 
{ 
    void DoZ(); 
} 

y me gustaría escribir algo como esto:

foreach(var obj in objects.OfType<BaseObject and IX>) 
{ 
    obj.DoStuff(); 
    obj.DoX(); 
} 

(Ej llevo a cabo un algoritmo específico para los objetos de tipo BaseObject y IX sin tener que hacer typecasts allí)

¿es posible hacerlo en C#? ¿Cuál es la solución más elegante?

Puedo hacer esto:

foreach(var obj in objects.OfType<IX>) 
{ 
    var baseobj = (BaseObject)obj.DoStuff(); 
    obj.DoX(); 
} 

pero me resulta desagradable.

Y podría necesitar aplicar operaciones específicas a los tipos que implementan, por ejemplo, la interfaz IX y la interfaz IZ.

foreach(var obj in objects.OfType<BaseType and IX and IZ>) 
{ 
    obj.DoStuff(); 
    obj.DoX(); 
    obj.DoZ(); 
} 

Respuesta

4

Una manera posible es utilizar dynamic:

foreach(var xz in objects.Where(o => o is IX && o is IZ).Select(o => (dynamic)o)) 
{ 
    xz.DoStuff(); 
    xz.DoX(); 
    xz.DoZ(); 
} 

Por supuesto, todavía puede lanzar si no desea utilizar dynamic:

foreach(var xz in objects.Where(o => o is IX && o is IZ)) 
{ 
    xz.DoStuff(); 
    ((IX)xz).DoX(); 
    ((IZ)xz).DoZ(); 
} 
+0

Creo que perdería intellisense allí, ¡pero se ve genial! – JBeurer

+0

@JBeurer, sí, es una compensación :) –

2

Con estas clases e interfaces ...

List<BaseObject> objects; 

class BaseObject 
{ 
    public void DoStuff(); 
} 

interface IX 
{ 
    public void DoX(); 
} 

interface IY 
{ 
    public void DoY(); 
} 

interface IZ 
{ 
    public void DoZ(); 
} 

class X : BaseObject, IX { } 
class Y : BaseOjbect, IY { } 
class Z : BaseObject, IZ { } 
class XY : BaseObject, IX, IY { } 

Asumming que pueblan ojbect con cualquier clase definido anteriormente

foreach (var o in objects) 
{ 
    o.DoStuff(); 
    if (o is IX) 
    ((IX)o).DoX(); //executed on class X and XY 
    if (o is IY) 
    ((IY)o).DoY(); //excuted on class Y and XY 
    if (o is IZ) 
    ((IZ)o).DoZ(); //excuted on class Z 
} 

Eso debería hacer lo que busca.

ACTUALIZADO por su comentario.

foreach (var o in objects) 
{ 
    o.DoStuff(); 
    if (o is IX and o is IY) 
    { 
    // do something different only for XY 
    } 
    else if (o is IX) 
    ((IX)o).DoX(); //executed on class X 
    else if (o is IY) 
    ((IY)o).DoY(); //excuted on class Y 
    else if (o is IZ) 
    ((IZ)o).DoZ(); //excuted on class Z 
} 

ACTUALIZADO Sin la conversión de tipos.

Requiere otra interfaz. (Además, DoStuff() debería estar en todos los IX, IY, IZ).

interface IXY : IX, IY { } 

//  This IXY could be var, but just strongly typing it for example 
foreach (IXY o in objects.OfType(IXY) 
         .Cast<IXY>()) 
{ 
    o.DoStuff(); 
    o.DoX(); 
    o.DoY(); 
} 

También puede hacer calle detrás LOCO como:

foreach (var obj in objects.OfType<IX>() 
          .OfType<IY>() 
          .OfType<IZ>() 
          .Select(o => new 
            { 
             AsIX = (IX)o, 
             AsIY = (IY)o, 
             AsIZ = (IZ)o 
            }) 
{ 
    obj.AsIX.DoX(); 
    obj.AsIY.DoY(); 
    obj.AsIZ.DoZ(); 
} 
+0

En realidad no, quiero realizar una Algoritmo específico solo para los objetos que implementan las interfaces X e Y. – JBeurer

+0

Después de la actualización, tiene toda la razón, pero desafortunadamente su solución involucra una gran cantidad de tipos de difusión, que es exactamente lo que quería evadir :( – JBeurer

+0

He actualizado según su comentario. ¿Por qué necesitaría la variable de cada para ser tipo fundido dentro del bucle, parece innecesariamente complicado (pero posible). –

0

Dentro DoStuff. Puede hacer

if (this is IX) { (this as IX).DoX(); } 

... y hacer eso para otras interfaces.

2

No es posible hacer EXACTAMENTE lo que quiere.

Esto es lo que PUEDE hacer.

En primer lugar, desea operar solo en los tipos que implementan IX, IY, Y IZ? Luego solo encadena tus métodos .OfType.OfType filtrará la lista para usted:

foreach (var obj in objects.OfType<IX>().OfType<IY>().OfType<IZ>()) { 

Desafortunadamente, obj sólo será de tipo fuerte IZ, porque no hay tal cosa como un tipo que implementa las 3 interfases. Por lo que aún tienen que desechar, pero tiene la garantía de que el elenco funcionará:

foreach (var obj in objects.OfType<IX>().OfType<IY>().OfType<IZ>()) { 
    ((IX)obj).DoX(); 
    ((IY)obj).DoY(); 
    ((IZ)obj).DoZ(); // Note, You could also just do obj.DoZ(), but consistency looks better. 
} 
+0

Sí, así es como está escrito ATM – JBeurer

+0

Bueno, eso es tan bueno como se pone :-) –

2

La forma más concisa para evitar una gran cantidad de fundición y ensayos de tipo es introducir un método de extensión que lo hace por ti.

Prueba esto:

public static void DoAs<T>(this object @this, Action<T> action) 
    where T : class 
{ 
    var t = @this as T; 
    if (t != null) 
    { 
     var a = action; 
     if (a != null) 
     { 
      a(t); 
     } 
    } 
} 

Ahora, supongo que en su último ejemplo de bucle que sólo desea ejecutar el bucle si el objeto implementa tanto IX & IZ. Por lo que sería luego escribir:

foreach(var obj in objects) 
{ 
    obj.DoAs<IX>(x => 
     x.DoAs<IZ>(z => 
     { 
      obj.DoStuff(); 
      x.DoX(); 
      z.DoZ(); 
     })); 
} 

Su Nombre/Segundo ciclo no necesita ningún nuevo método de extensión ya que creo que debe ser lo suficientemente simple:

foreach(var obj in objects.OfType<IX>()) 
{ 
    (obj as BaseObject).DoStuff(); 
    obj.DoX(); 
} 

espero que esto ayude.

+0

¡Una idea única! Hasta ahora, esto es lo más cercano posible. – JBeurer