Относитесь к классу так, как если бы он был определен с помощью интерфейса

StackOverflow https://stackoverflow.com/questions/470084

Вопрос

У меня есть 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 # позволить мне относиться к ruffles и scrufy как к IAnimal без необходимости писать :IAnimal при написании класса.

Спасибо!

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

Это было полезно?

Решение

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

Другие советы

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

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

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

Вы могли бы создать адаптер, который получает доступ к "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 )};

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

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

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

  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 #, чем я, сможет сделать то же самое с reflection.

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

Нет.Вы никак не можете заставить C # позволить вам делать это, не имея ни конкретного базового класса, ни интерфейса.

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top