Есть ли способ объявить переменную, которая реализует несколько интерфейсов в .Net? - PullRequest
18 голосов
/ 03 мая 2010

Похоже на этот вопрос Java . Я хотел бы указать, что переменная реализует несколько интерфейсов. Например

private {IFirstInterface, ISecondInterface} _foo;

public void SetFoo({IFirstInterface, ISecondInterface} value)
{
    _foo = value;
}

Требования:

  • У меня нет возможности добавить интерфейс для большинства типов, которые будут переданы в Foo. Поэтому я не могу создать третий интерфейс, который наследуется от IFirstInterface и ISecondInterface.
  • Я бы не хотел делать обобщающий класс, если это возможно, потому что тип Foo не имеет большого отношения к классу, и пользователь вряд ли узнает его во время компиляции.
  • Мне нужно использовать foo для доступа к методам в обоих интерфейсах позже.
  • Я бы хотел сделать это безопасным для компилятора способом, то есть не пытаться привести к интерфейсу непосредственно перед тем, как пытаться его использовать. Если foo не реализует оба интерфейса, некоторые функции не будут работать должным образом.

Возможно ли это?

Редактировать : Я хотел этого несколько раз по разным причинам. В данном случае это происходит потому, что я меняю набор свойств с ObservableCollections на ReadOnlyObservableCollections. У меня есть вспомогательный класс, который создает проекцию из источника ObservableCollection в другую коллекцию. Поскольку ReadOnlyObservableCollection не наследуется от ObservableCollection, и мне нужны только операции в IList и INotifyCollectionChanged, я надеялся сохранить мою коллекцию источников как комбинацию этих двух интерфейсов, а не требовать ObservableCollection.

Ответы [ 4 ]

17 голосов
/ 03 мая 2010

Если вы хотите ограничить свой метод SetFoo только теми параметрами, которые реализуют эти два интерфейса, то вы в удаче:

public void SetFoo<T>(T value) where T : IFirstInterface, ISecondInterface
{
    _foo = value;
}

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

Вы сказали, что хотели бы избежать использования приведений для безопасности во время компиляции; но я думаю, что если вы найдете способ обеспечить инициализацию _foo, используя SetFoo выше, вы можете разыграть все, что хотите, с душевным спокойствием.


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

Другой идеей было бы, поскольку, похоже, этот _foo объект является членом класса (я основываю это предположение на _ в имени переменной), вы можете определить свой класс следующим образом:

class YourClassThatHasAFoo<T> where T : IFirstInterface, ISecondInterface {
    private T _foo;

    public void SetFoo(T value) {
        _foo = value;
    }
}

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

6 голосов
/ 03 мая 2010

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

Один вариант МОЖЕТ быть возможен с использованием обобщений, хотя в действительности это будет работать только для функции, а не для свойства.

public void Foo<T>(T arg)
    where T : IFirstInterface
    where T : ISecondInterface
{
    ...
}

Используя вывод типа, вы должны иметь возможность назвать его так:

ConcreteType bar = new ConcreteType(); // this implements both

Foo(bar);
2 голосов
/ 09 февраля 2011

Можно определить интерфейсы, так что их произвольные комбинации могут потребоваться и использоваться безопасным для типов образом. Ключ заключается в том, чтобы определить интерфейс ISelf (Of Out T), один элемент которого, Self, является свойством, посредством которого объект возвращает себя как T, а затем для каждого интерфейса IFoo объявить соответствующий универсальный вид формы IFoo (Of Out T), который наследуется от IFoo и ISelf (Of T). Класс Wowzo, который реализует ISelf (Of Wowzo), IFoo (Of Wowzo), IBar (Of Wowzo) и IBoz (Of Wowzo), будет реализовывать IFoo (Of IBar), IBar (Of IFoo), IFoo (IBoz (Of IBar)), IFoo (от IBar (от IFoo (от IBar (от IBoz (от IBar))))) и т. Д. И может при необходимости быть приведенным к любому такому типу. Если Thing - это IFoo (Of IBar (Of IBoz)), его можно использовать непосредственно как IFoo, или использовать Thing.Self в качестве IBar, или использовать Thing.Self.Self в качестве IBoz.

Пример того, как таким образом составить три интерфейса вместе с C #:

public interface ISelf<out T>
{
    T Self { get; }
}
interface IA { }
interface IA<T> : IA, ISelf<T> { }
interface IB { }
interface IB<T> : IB, ISelf<T> { }
interface IC { }
interface IC<T> : IC, ISelf<T> { }
class ConcreteThing
    : IA<ConcreteThing>
    , IB<ConcreteThing>
    , IC<ConcreteThing>
{
    public ConcreteThing Self => this;
}
public class ReferencingObject
{
    IA<IB<IC>> Thing { get; }

    void Method()
    {
        IA a = Thing;
        IB b = Thing.Self;
        IC c = Thing.Self.Self;
    }
}
2 голосов
/ 03 мая 2010

Переменная в .NET имеет тип, и типы могут реализовывать несколько интерфейсов.Примерно так:

public interface Interface1 { }
public interface Interface2 { }
public class SupportsMuliple : Interface1, Interface2 { }

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

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