Как я могу привести интерфейс как его тип в C #? - PullRequest
3 голосов
/ 27 августа 2009

У меня есть свойство, которое возвращает интерфейс. Во время отладки я могу разбить то, что было возвращено, и, хотя это интерфейс, Visual Studio достаточно умен, чтобы знать производный тип, которым он на самом деле является. Я предполагаю, что это использует отражение или что-то. Я не уверен. У меня вопрос, могу ли я иметь ту же самую информацию, доступную мне во время выполнения, чтобы я мог создать переменную соответствующего типа и привести интерфейс как этот? Вот что я говорю:

IPreDisplay preDisplay = cb.PreDisplay;

Если preDisplay - это RedPreDisplay, я бы хотел иметь возможность кодировать

RedPreDisplay tmp = preDisplay as RedPreDisplay;

Или, если preDisplay был GreenPreDisplay ...

GreenPreDisplay tmp = preDisplay as GreenPreDisplay;

и т.д ... Я хотел бы, по возможности, избежать беспорядочного переключения, и если бы я мог использовать непатентованные формы, это было бы здорово.

Если у вас есть какие-либо советы или примеры того, как я могу это сделать, пожалуйста, поделитесь.

Ответы [ 5 ]

17 голосов
/ 27 августа 2009

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

Чтобы ответить на ваш вопрос напрямую, нет - вы не можете сделать это без какого-либо if / else или условного, потому что вы должны быть явными со статическими типами. Вы можете использовать рефлексию для вызова метода, но так как вам, кажется, нужно вызывать что-то, что интерфейс не поддерживает - но некоторые объекты это делают - вам все равно придется кодировать условие для статического типа для вызова этого метода. Просто закодируйте типы напрямую.

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

IPreDisplay display = cb.PreDisplay;
IOtherInterface displayAsOther = display as IOtherInterface;
if(displayAsOther != null)
{
    displayAsOther.OtherMethod();
}
6 голосов
/ 27 августа 2009

Целью использования интерфейсов является то, что исполняемый код не должен знать точный тип . Попробуйте раскрыть всю информацию, которая может вам понадобиться, через сам интерфейс, так что вам не нужно приводить.

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

4 голосов
/ 27 августа 2009

В зависимости от того, что вы пытаетесь сделать, вам, вероятно, следует добавить метод / свойство действия в интерфейс, чтобы вам не нужно было знать тип - это полиморфизм.

например:

 IResultLiteEntity preDisplay = cb.PreDisplay;
 preDisplay.Render (); // New Render method on the interface...
3 голосов
/ 27 августа 2009

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

Однако, если для этого есть веские причины, у вас есть только несколько вариантов:

  1. Использовать отражение. Обычно это медленный и подверженный ошибкам код, который также хрупок при изменении реализации (т. Е. Методы переименовываются и т. Д.).
  2. Используйте шаблон if / else if / else для отправки на основе проверок типа во время выполнения. Это в значительной степени ваш единственный выбор в версиях C # до 4.0.
  3. Если вы используете C # 4.0, вы можете использовать назначение объекта динамической переменной , а во время выполнения - перегруженному методу, сигнатура которого варьируется для каждого из поддерживаемых типов (см. Пример ниже).

Вот пример динамической отправки C # 4.0:

void Foo()
{
  dynamic preDisplay = cb.PreDisplay;
  DoSomethingWith( preDisplay );  // runtime dispatch using dynamic runtime (DLR)
}

void DoSomethingWith( RedPreDisplay r ) { ... }  // code specific to RefPreDisplay
void DoSomethingWith( GreenPreDisplay g ) { ... } // code specific to GreenPreDisplay
void DoSomethingWIth( IPreDisplay o ) { ... }  // catch-all
3 голосов
/ 27 августа 2009

@ Рекс М абсолютно прав. Проблема заключается в вашем коде и базовой структуре. Как правило, вы не должны делать то, что вы пытаетесь сделать; код против интерфейса только .

Тем не менее, есть оператор is, который может помочь вам, если вы унаследовали плохой код и вам нужно его обезьяна-патч. Например:

if(myInstance is MyBaseType)
{
  MyBaseType myInstanceAsBaseType = myInstance as MyBaseType;
  // handle MyBaseType specific issue
}
else if(myInstance is MyOtherBaseType)
{
  MyOtherBaseType myInstanceAsOtherBaseType = myInstance as MyOtherBaseType;
  // handle MyOtherBaseType specific issue.
}

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

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