Доступ к методам интерфейса без обращения к классу
Скажем, у меня есть такой интерфейс в проекте под названием "Интерфейс":
public interface TestInterface
{
string Operation();
}
и класс, который реализует это. Этот класс находится в другом проекте "Класс":
public class TestClass : TestInterface
{
public TestClass() { }
public string Operation()
{
return "This is an Operation";
}
}
Мой клиент делает что-то вроде этого (что опять-таки в другом проекте "Клиент"):
class Program
{
static void Main(string[] args)
{
TestInterface i = new TestClass();
i.Operation();
}
}
Мой вопрос связан с этой строкой:
TestInterface i = new TestClass();
Добавив эту строку, я фактически вынужден добавить ссылки как на "Интерфейс", так и на "Класс" проектов из моего "Клиентского" проекта. Так зачем все это суетиться? Разве я не могу напрямую ссылаться на "Класс", не оставляя "Интерфейс" между ними? Есть ли способ получить доступ к методам только через интерфейс (без ссылки на класс реализации)? Я что-то здесь упускаю?
5 ответов
Есть ли способ получить доступ к методам только через интерфейс
Да, есть. Вы можете динамически загрузить сборку с TestClass
не ссылаясь на него, создайте его экземпляр с помощью Activator.CreateInstance
и приведите его к типу интерфейса:
var assembly = Assembly.Load(...);
var typeFromAssembly = assembly.GetTypes()...;
var myInterfaceVar = (TestInterface)Activator.CreateInstance(typeFromAssembly);
... или... вы можете использовать одну из существующих DI-платформ (например, MEF) и делать то же самое более правильным образом:
[Import]
private TestInterface myInterfaceField;
или же:
var myInterfaceVar = compositionContainer.GetExportedValue<TestInterface>();
В зависимости от того, как вы предпочитаете, вы можете задать более конкретный вопрос.
Сделать ваш код полностью независимым от реализации TestInterface
использовать инверсию зависимости. Этого можно достичь с помощью некоторой структуры внедрения зависимостей.
Например, с Unity вы можете настроить реализацию через XML
<register type="TestInterface"
mapTo="Foo.Bar.TestClass, Foo.Bar" />
И ваш код будет зависеть только от Unity (без ссылок на реализацию):
TestInterface i = Container.Resolve<TestInterface>();
В этом конкретном примере нет никаких преимуществ.
Но представьте метод:
public void Foo(ITestInterface handler)
{
handler.Operation();
}
Сейчас, Foo
работает только на интерфейсе, и ему все равно, какой конкретный класс реализует этот интерфейс. Теперь вы можете позвонить Foo
с экземпляром TestClass
или с TestClass2
, который может быть определен в другой сборке.
У вас есть интерфейс, так что ваше приложение может иметь plug in's
..
Таким образом, вы делите свой интерфейсный dll с любым, кто хочет создать приложение-плагин для вашего приложения, а затем вы можете привести этот новый класс плагинов к интерфейсу и вызвать методы для него.
Если вы не приведете класс к интерфейсу, как же вы собираетесь заставить класс плагина работать для вашего приложения?
Вы можете достичь поведения, которое вы описали, используя МОК. Unity - это контейнер внедрения зависимостей, который позволяет создавать экземпляры, не создавая экземпляры вручную.
Например, если бы вы зарегистрировали свой класс и интерфейс на единицу, вы бы напрямую использовали интерфейс;
TestInterface i = Container.Resolve<TestInterface>();