Подтвердить что ты не робот

Есть ли способ объявить переменную, которая реализует несколько интерфейсов в .Net?

Как и этот вопрос 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.

4b9b3361

Ответ 1

Если вы хотите ограничить свой метод 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, а не только любой класс который реализует ваши два интерфейса).

Ответ 2

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

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

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

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

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

Foo(bar);

Ответ 3

У переменной в .NET есть тип, и типы могут реализовывать несколько интерфейсов. Что-то вроде этого:

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

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

Ответ 4

Можно определить интерфейсы так, чтобы их можно было использовать и использовать в безопасном типе. Ключ состоит в том, чтобы определить интерфейс 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 (Of IBoz (Of IBar)), IFoo (Of IBar (Of IBar (Of IBar (Of IBar))))) и т.д. И при необходимости может быть приложено к любому типу. Если Thing является IFoo (из IBar (Of IBoz)), его можно использовать непосредственно как IFoo или использовать Thing.Self как IBar или использовать Thing.Self.Self как IBoz.

Пример того, как скомпоновать три интерфейса вместе с С#:

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;
    }
}