Могу ли я определить / ограничить член как реализацию двух интерфейсов, без обобщений? - PullRequest
4 голосов
/ 27 января 2012

Следующий код показывает, что я хотел бы сделать; то есть я хотел бы ограничить anObject, чтобы его можно было использовать в качестве параметра для различных методов с использованием IInterfaceOne или IInterfaceTwo, где ни один не наследуется от другого.

public interface IInterfaceOne { }
public interface IInterfaceTwo { }

public class Implementation : IInterfaceOne, IInterfaceTwo
{
}

public interface IInterfaceOneAndTwo : IInterfaceOne, IInterfaceTwo { }

public class UsingImplementation
{
    IInterfaceOneAndTwo anObject = (IInterfaceOneAndTwo)(new Implementation()); //fails because Implementation doesnt acctually implement IInterfaceOneAndTwo
}

Однако этот пример завершается ошибкой, поскольку IInterfaceOneAndTwo является самостоятельным интерфейсом, а реализация не реализует его.

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

Есть ли способ сказать, anObject должен реализовать IInterfaceOne и IInterfaceTwo, без использования IInterfaceOneAndTwo?

Ответы [ 5 ]

5 голосов
/ 27 января 2012

Не так, как сейчас.Этой способностью обладают только общие ограничения.

Вы можете переписать ее, чтобы использовать общие:

public class UsingImplementation<T>
   where T : IInterface1, IInterface2, new()
{
    T anObject = new T();

    void SomeMethod() {
       anObject.MethodFromInterface1();
    }
}
2 голосов
/ 28 января 2012

Вы не можете сделать это в C # без обобщений, но есть альтернативный обходной путь до , решающий проблему без обобщений , который здесь не упоминался и может подойти вам. Этот стиль часто используется вместе с принципом IoC. Вы можете ввести один и тот же объект дважды. Позвольте мне немного изменить ваш образец ...

public interface IInterfaceOne { void Hello(); } 
public interface IInterfaceTwo { void World(); } 

public class Implementation : IInterfaceOne, IInterfaceTwo
{
   public void Hello() { };
   public void World() { };
} 

public class UsingImplementation 
{
   private readonly IInterfaceOne one;
   private readonly IInterfaceTwo two;

   public UsingImplentation(IInterfaceOne one, IInterfaceTwo two)
   {
      this.one = one;
      this.two = two;
   }

   // do the stuff you want to do with an IInterfaceOne using field one
   public DoSomeThingWithOne() { one.Hello(); }

   // do the stuff you want to do with an IInterfaceTwo using field two
   public DoSomeThingWithTwo() { two.World(); }
}

Тогда вы могли бы связать вещи так:

var oneAndTwo = new Implementation();

var a = new UsingImplementation(oneAndTwo, oneAndTwo);

// operates on the first param (which is the same as the second)
a.DoSomeThingWithOne();

// operates on the second param (which is the same as the first)
a.DoSomeThingWithTwo();

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

Таким образом, вам не нужно создавать дополнительный интерфейс, объединяющий InterfaceOne и InterfaceTwo, два.

2 голосов
/ 27 января 2012

Вы также можете иметь универсальные методы, а не только универсальные классы

public void DoSomething<T>(T value)
    where T : IInterface1, IInterface2
{
    value.DoInterface1Things();
    value.DoInterface2Things();
}

или

public void DoSomething<T>()
    where T : IInterface1, IInterface2, new()
{
    T anObject = new T();
}
1 голос
/ 27 января 2012

«Входящие» параметры универсального класса и параметры универсального метода могут комбинировать типы, но нет возможности для переменных или полей представлять «составные» типы.Кроме того, чтобы передать объект параметру универсального типа, который объединяет несколько ограничений, объект должен быть приведен к типу, который фактически реализует все эти ограничения.Это может быть сложно.

Например, предположим, что класс Foo и Bar оба реализуют Intf1 и Intf2.Хочется написать функцию AddToList<T>(thing as T) where T:Intf1,Intf2.Такая функция прекрасно подходит для объектов типа Foo или Bar.Предположим, однако, что кто-то хочет использовать такую ​​функцию для добавления всех объектов в один и тот же список (который может быть смесью Foo, Bar и любого числа других типов, которые также могут реализовывать Intf1 и Intf2), а затем передают эти объекты в функцию, параметр которой также ограничен для реализации Intf1 и Intf2.Можно привести к Foo любой объект, который оказался Foo, и привести к Bar любой объект, который оказался Bar, но если записаны другие типы, которые также обрабатывают Intf1 и Intf2, было бы трудно иметь с ними дело.

Возможно, решить проблему несколько неловко, не используя Reflection или другие подобные трюки.Определите интерфейс IActUpon<Base1, Base2> с помощью метода ActUpon<thingType>ActUpon(thingType thing) where thingType: Base1, Base2.Реализации такого метода смогут передавать параметр thing другим методам, для которых требуется универсальный параметр метода, ограниченный Base1 и Base2.Самые большие трудности при таком подходе состоят в том, что нужно написать отдельный код для каждого возможного числа ограничений, и что во многих местах, где можно было бы использовать лямбда-выражение, вместо этого придется написать реализацию IActUpon....

1 голос
/ 27 января 2012

Если это желательно, то должна быть логическая связь между IInterfaceOne и IInterfaceTwo, и реализующий класс должен реализовать комбинированный интерфейс:

class Implementation : IInterfaceOneAndTwo { ... }

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

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