Фильтрация списка с использованием нескольких классов / интерфейсов - PullRequest
5 голосов
/ 24 ноября 2011

В основном у меня есть список объектов, где каждый объект может реализовывать свой набор интерфейсов:

List<BaseObject> objects;

class BaseObject
{
  public void DoStuff();
}

interface IX
{
  void DoX();
}

interface IY
{
  void DoY();
}

interface IZ
{
  void DoZ();
}

И я хотел бы написать что-то вроде этого:

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

(например, я выполняю определенный алгоритм для объектов типа BaseObject и IX без необходимости выполнять там типы)

Возможно ли это сделать в C #? Какое самое элегантное решение?

Я могу сделать это:

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

Но я нахожу это уродливым.

И мне может понадобиться применить определенные операции к типам, которые реализуют, например, интерфейс IX и интерфейс IZ.

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

Ответы [ 5 ]

4 голосов
/ 24 ноября 2011

Один из возможных способов - использовать dynamic:

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

Конечно, вы все равно можете разыграть, если не хотите использовать dynamic:

foreach(var xz in objects.Where(o => o is IX && o is IZ))
{
    xz.DoStuff();
    ((IX)xz).DoX();
    ((IZ)xz).DoZ(); 
}
2 голосов
/ 24 ноября 2011

Самый краткий способ избежать большого количества приведения и тестирования типов - это ввести метод расширения, который его обрабатывает.

Попробуйте это:

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);
        }
    }
}

Теперь, я полагаюв последнем примере цикла, который вы хотите выполнить только в том случае, если объект реализует оба значения IX & IZ.Поэтому вы должны написать:

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

Ваш первый / средний цикл не нуждается в каком-либо новом методе расширения, так как я думаю, что это должно быть достаточно просто:

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

Надеюсь, это поможет.

2 голосов
/ 24 ноября 2011

Невозможно делать именно то, что вы хотите.

Вот что вы МОЖЕТЕ сделать.

Во-первых, вы хотите работать только с типами, которые реализуют IX, IY и AND IZ? Затем просто соедините вместе ваши .OfType методы. OfType отфильтрует список для вас:

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

К сожалению, obj будет только строго типизирован IZ, потому что нет такого понятия, как тип, который реализует все 3 интерфейса. Таким образом, вы все еще должны разыграть, но вы гарантировано, что приведение будет работать:

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.
}
2 голосов
/ 24 ноября 2011

С этими классами и интерфейсами ...

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 { }

Как только вы заполняете ojbect любым Классом , определенным выше

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
}

Это должно делать то, что выищу.

ОБНОВЛЕНО за ваш комментарий.

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
}

ОБНОВЛЕНО Нет приведения типов.

Требуется другой интерфейс.(Кроме того, DoStuff () должен быть на всех 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();
}

Вы также можете сделать что-нибудь CRAZY , например:

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 голосов
/ 24 ноября 2011

В пределах DoStuff. Можете ли вы сделать

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

... и сделать это для других интерфейсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...