Обрабатывать класс так, как если бы он был определен с помощью интерфейса - PullRequest
2 голосов
/ 22 января 2009

У меня есть 100 классов, которые имеют некоторые похожие элементы и некоторые уникальные. Я создал интерфейс, который называет эти похожие элементы, например: интерфейс IAnimal. Что я обычно делаю, это:

class dog : IAnimal

Но есть 100 классов, и мне не хочется проходить их все и искать те, к которым можно применить IAnimal.

Что я хочу сделать, это:

dog scruffy = new dog();
cat ruffles = new cat();

IAnimal[] animals = new IAnimal[] {scruffy as IAnimal, ruffles as IAnimal} // gives null

или

IAnimal[] animals = new IAnimal[] {(IAnimal)scruffy, (IAnimal)ruffles} //throws exception

затем сделайте

foreach (IAnimal animal in animals)
{
   animal.eat();
}

Есть ли способ заставить c # позволять мне рассматривать оборки и неряшливости как IAnimal без необходимости писать: IAnimal при написании класса.

Спасибо!

РЕДАКТИРОВАТЬ (не ленивый): классы генерируются из sql хранимых метаданных proc, что означает, что каждый раз, когда они генерируются, мне придется возвращаться и добавлять их или модифицировать генератор кода, чтобы определить членов, которые находятся в интерфейс, на самом деле это неплохая идея. Я надеялся, что был какой-то общий подход или что-то в этом роде.

Ответы [ 11 ]

10 голосов
/ 22 января 2009

То, что вы просите, называется "утка" и не является частью C #, я боюсь. Любое решение будет включать в себя рефлексию и просмотр свойств. Думаю, будет быстрее проверить классы вручную.

Хотя это был бы интересный проект, чтобы попробовать.

6 голосов
/ 22 января 2009

Эту проблему можно решить с помощью частичных классов : пусть машинно-сгенерированный / восстановленный код будет в одном исходном файле каждого класса, а часть с ручным кодированием (определяющая подклассы из IAnimal) - в другом .

4 голосов
/ 22 января 2009

Вы можете создать адаптер, который обращается к «eat» через отражение, набрав утку бедняка:

public class Adapter<T> : IAnimal
{
   private T x;

   Adapter(T x)
   {
     this.x = x;
   }

   public void eat()
   {
     x.GetType().GetMethod("eat").Invoke(this);
   }
}

Тогда вы можете использовать это так:

dog scruffy = new dog();
cat ruffles = new cat();

IAnimal[] animals = new IAnimal[] {new Adapter<dog>(scruffy), new Adapter<cat>(ruffles )};
4 голосов
/ 22 января 2009

Решение состоит в том, чтобы изменить генератор кода. В настоящее время это слишком упрощенно для ваших нужд. Следует добавить реализацию интерфейса, если в классе присутствуют соответствующие свойства и / или методы.

1 голос
/ 22 января 2009

Если генератор кода генерирует классы как частичные, вы можете добавить другое частичное определение, которое реализует интерфейс.

1 голос
/ 22 января 2009

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

1 голос
/ 22 января 2009

Если вы действительно не можете изменить класс cat по уважительным причинам,

Вы можете написать адаптер для cat, унаследованный от IAnimal, т.е.:

  class cat_adapter : IAnimal
  {
       private cat baseCat;
       public cat_adapter( cat aCat)
       {
           baseCat = aCat;
       }

       // Implement IAnimal interface and redirect to baseCat
       public void eat()
       {
            baseCat.munchOnMeowMix();
       }

  }

В C ++ вы можете использовать шаблоны, предполагая, что все ваши сгенерированные классы должны иметь одну и ту же функцию с именем:

  template <class BaseType>
  class CAnimalAdapter : public IAnimal
  {
  private:
        BaseType* m_baseInstance;
  public:
        CAnimalAdapter(BaseType* baseInstance) :
            m_baseInstance(baseInstance)
        {
        }

        void eat()
        {
            // You will get a compiler error if BaseType doesn't have this implemented
            baseInstance->doEat();                
        }

  }

Может быть, кто-то более C # -пи, чем я, может сделать то же самое с отражением.

0 голосов
/ 22 января 2009

Если вы реализуете сгенерированные классы как частичные классы, вы можете реализовать интерфейсы для этих классов, а затем изменить генератор кода, чтобы генерировать только половину частичного класса. Это позволит вам восстановить ваши классы без потери реализации интерфейса (или любой другой пользовательской логики, которую вы решите добавить).

Этот тип подхода заключается в том, как LINQ to SQL генерирует файлы классов.

0 голосов
/ 22 января 2009

Ваши классы должны реализовывать интерфейс IAnimal. Метод / свойство с одним и тем же именем в двух разных классах не считается эквивалентным, если только они не являются реализациями интерфейса / базового класса.

0 голосов
/ 22 января 2009

Если вы уже внедрили метод и просто хотите реализовать интерфейс, вы можете использовать регулярное выражение с командой replace in files в вашей IDE.

В противном случае взгляните на методы расширения. Они могут соответствовать вашим потребностям.

...