2009-07-08 2 views
0

Следующий пример плох, потому что он имеет повторение кода. Например, если нам понадобится CheckExpirationDate перед употреблением, нам нужно добавить вызов функции в несколько мест. Если нам нужно добавить charley и stan на вечеринку, код будет очень быстрым.Обработка ряда именованных переменных того же типа

if (john != designatedDriver) 
{ 
    beer.Open(); 
    john.Drink(beer); 
} 
if (bob != designatedDriver) 
{ 
    cider.Open(); 
    bob.Drink(cider); 
} 

Решение может заключаться в создании массива этих объектов. Это очень просто, если вам нужно управлять одним объектом за раз. Например, если все проходит вокруг одной бутылки пива:

beer.Open(); 
foreach (Dude i in new Dude[] { john, bob }) 
{ 
    i.Drink(beer); 
} 

Чтобы переписать исходный пример, мы можем использовать KeyValuePair. Не особенно красиво, но стоит того, чтобы больше двух парней или если процедура выпивки достаточно длительная (дегустация вин).

foreach (KeyValuePair<Dude, Booze> i in KeyValuePair<Dude, Booze>[] { 
    new KeyValuePair<Dude, Booze>(john, beer), 
    new KeyValuePair<Dude, Booze>(bob, cider) }) 
{ 
    if (i.Key != designatedDriver) 
    { 
     i.Value.Open(); 
     i.Key.Drink(i.Value); 
    } 
} 

Но что, если нам нужно работать три или более объектов в то время? Например, john drink beer и ест nachos. Мы можем прибегнуть к объявлению пользовательских типов, но это сделает код зависимым от другого кода где-то далеко. Мы могли бы поместить каждый тип в свой собственный массив и сделать старый добрый цикл for и получить доступ к объектам с индексами, но это будет означать создание названных массивов (Dude[] dudes = new Dude { john, bob };).

Что я прошу, Есть ли какой-то причудливый трюк C#, который позволил бы мне сделать это очень чисто и компактно? Анонимные типы? Что нибудь?

+0

Ваш первый и третий образцы кода не эквивалентны, как вы говорите. В третьем примере боб никогда не пьет свой сидр ... – LBushkin

+0

Продумайте, пожалуйста. – CannibalSmith

ответ

2

Вы упомянули анонимные типы, это следует сделать трюк:

foreach (var i in new[] { 
          new{Dude=john, Booze=beer, Food=nachos}, 
          new{Dude=bob, Booze=cider, Food=chips} 
         }) 
{ 
     if (i.Dude != designatedDriver) 
     { 
       i.Booze.Open(); 
       i.Dude.Drink(i.Booze); 
       i.Dude.Eat(i.Food); 
     } 
} 
+0

Назначенный водитель тоже не может поесть? Это просто! – LukeH

+0

Хммм ... Чтобы защитить от пищевого отравления? –

0

Ну, если у вас есть три отдельных коллекции Dudes, Boozes и Foods, вы можете использовать запрос LINQ и создать анонимный тип. Тогда вам не придется беспокоиться о создании нового типа и любых зависимостей, которые могут быть созданы этим. Вот пример:

// assumes existing collections called "boozes" "dudes" and "foods" 
var qry = from booze in boozes 
      from dude in dudes 
      from food in foods 
      select new { 
       Dude = dude, 
       Booze = booze, 
       Food = food 
      }; 

foreach(var item in qry) 
{ 
    if(!item.Dude.IsDesignatedDriver) 
    { 
     item.Booze.Open(); 
     item.Food.Cook(); 
     item.Dude.Drink(item.Booze); 
     item.Dude.Eat(item.Food); 
    } 
} 

Недостатки:

  • анонимный тип должен быть использован из блока вызова - вы не можете вернуться анонимные типы из метода.
  • Вам нужны уже существующие коллекции.
  • Введенный код создает спаривание для каждого варианта с чуваком, едой и выпивкой. На самом деле вы этого не хотите. Тем не менее предложение о помощи может помочь.
+0

Почему бы просто не добавить «где! Dude.IsDesignatedDriver» в верхний запрос? –

+0

Это дает декартово произведение dude x drink x food, когда я думаю, что вы хотели, чтобы они застряли. Мы добавляем оператор последовательности Zip в библиотеку методов расширения для следующей версии фреймворка. –

0

Я не знаю, сколько у вас контроля над своими классами, но я хотел бы создать интерфейс (или использовать базовый класс , в зависимости от конкретных потребностей), называемый IInjestible (или что-то), а затем дать каждому чуваку список (List <>, IEnumerable <>, что бы там ни было) типа IInjestible.Тогда вы могли бы сделать что-то вроде:

 
foreach (Dude dude in Dudes) 
{ 
    foreach(IInjestible snack in Snacks) 
    { 
    snack.Injest(dude); 
    } 
} 

Или с базового класса Закуска:

 
foreach(Dude dude in Dudes) 
{ 
    foreach(Snack snack in Snacks) 
    { 
    if(snack is Drink) {dude.drink(snack);} 
    if(snack is Food) {dude.eat(snack);} 
    } 
} 
0

Ну, кажется, что это что-то, что может быть исправлено путем изменения конструкции приложения, предполагая, что это это то, что можно сделать. Что вы можете сделать, это создать интерфейс, как следующее:

interface IConsumable{ }; 

Тогда вы можете иметь каждый из ваших расходуемых классов реализуют этот интерфейс, так что вам придется:

class Beer : IConsumable, IDrink { 
    public void Drink { ...; } 
} 

class Nachos : IConsumable, IFood { 
    public void East { ...; } 
} 

Затем в KeyValuePair вас может послать чувак и массив IConsumable объектов, как этот

foreach (KeyValuePair<Dude, IConsumable[]> i in KeyValuePair<Dude, IConsumale[]>[] { 
    foreach(IConsumable consumable in i.Value) { 
     if(consumable is IFood){ 
      //Code for food 
     } 
     if(consumable is IDrink){ 
      //Code for drinks 
     } 
     //You can even be more specific 
     if(consumable is Beer){ //Code for Beer } 
    } 
} 

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

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

Смежные вопросы