Как создать переменную типа, которая реализует несколько интерфейсов (или подмножество интерфейсов)? - PullRequest
0 голосов
/ 20 мая 2018

У меня есть сторонняя библиотека (dll, созданная с использованием C #), которая содержит интерфейсы

public interface I1 {
    void F1();
}
public interface I2 {
    void F2();
}
public interface I3 {
    void F3();
}
// and so on ...

и содержит класс

public class C : I1, I2, I3 // ...
{
    public void F1() { }    // implement I1.F1();
    public void F2() { }    // implement I2.F2();
    public void F3() { }    // implement I3.F3();
    // and so on ...
}

Я добавил ссылку наэта библиотека в моем проекте в VisualStudio и создала экземпляр класса C

C c = new C();
c.F1();    // ok
c.F2();    // ok
c.F3();    // ok

Переменная c позволяет вызывать методы всех интерфейсов (F1, F2, F3, ...).

Чтобы создать переменную, позволяющую вызывать только методы интерфейса I1, достаточно изменить тип переменной

    I1 c = new C();
    c.F1();    // ok
//  c.F2();    // error is ok
//  c.F3();    // error is ok

Но Теперь я хочу создать переменную некоторого типа, которая позволяет вызывать методы интерфейсов I1, I2 и не позволяет вызывать методы других интерфейсов

    I1_I2 c = new C();
    c.F1();    // ok
    c.F2();    // ok
//  c.F3();    // error is ok

Для этого,Я создал интерфейс

public interface I1_I2 : I1, I2 { }

Однако компилятор не позволяет мне выполнить присвоение

I1_I2 c = new C();    // Compile error : Cannot implicitly convert type 'C' to 'I1_I2'.

Я попытался явное преобразование

I1_I2 c = (I1_I2)new C();    // Runtime error : InvalidCastException was unhandled.

, но этопреобразование не может быть выполнено, потому что класс C не реализует I1_I2 (только I1 и I2 отдельно).


Одним из возможных решений этой проблемы может быть обертка класса

public class Wrapper : I1_I2
{
    private C _c = new C();
    public void F1() { _c.F1(); }
    public void F2() { _c.F2(); }
}
// ---------------------------------
    I1_I2 c = new Wrapper();
    c.F1();    // ok
    c.F2();    // ok
//  c.F3();    // error is ok

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


Поэтому мой вопрос: Как создать переменнуючто позволит мне сделать это:

    c.F1();    // ok
    c.F2();    // ok
//  c.F3();    // error is ok

?

Ответы [ 2 ]

0 голосов
/ 20 мая 2018

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

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

public void MySuperLogic()
{
    C c = new C();
    c.F1();
    c.F2(); 
    c.F3();
}

Вы не хотите разрешать вызов метода C.F3(), поэтому универсальный метод с ограничениями может помочьпутем рефакторинга метода, как показано ниже:

public void MySuperLogic<I>(I c) 
    where I: I1, I2 // <- The generic type parameter should implement interfaces I1 and I2.
{
    c.F1();
    c.F2(); 
    c.F3(); // <-- CS1061 : Compile time error.
}

CS1061 - это ошибка времени компиляции, которая говорит вам следующее:

CS1061 «I» не содержит определения для «F3» иневозможно найти метод расширения 'F3', принимающий первый аргумент типа 'I'.

Вы можете вызвать свой метод следующим образом:

var c = new C();
MySuperLogic(c);

Мы передали экземплярвведите C, который реализует I3 с помощью метода F3(), но из-за ограничений, определенных в MySuperLogic, вам не разрешено использовать F3(), поскольку он не существует в I1 и I2.

0 голосов
/ 20 мая 2018

Возможно, лучшим решением в этом случае является

public interface I1_I2 : I1, I2 { }

public class C_I1_I2 : C, I1_I2 { }

public static void Main() {
    I1_I2 c = new C_I1_I2();
    c.F1();    // ok
    c.F2();    // ok
//  c.F3();    // error is ok
}

Источник

...