leofun01
leofun01

Reputation: 235

How to create a variable of type that implement few interfaces (or subset of interfaces)?

I have a third-party library (dll, created using C#) that contains interfaces

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

and contains a class

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

I added a reference to this library in my project in VisualStudio and created an instance of class C

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

The variable c allows to call the methods of all interfaces (F1, F2, F3, ...).

To create a variable that allows to call only methods of interface I1, it is enough to change the type of variable

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

But now I want to create a variable of some type that allows to call methods of interfaces I1, I2 and does not allows to call methods of other interfaces

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

To do this, I created the interface

public interface I1_I2 : I1, I2 { }

However, the compiler does not allow me to make the assignment

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

I tried an explicit conversion

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

but this conversion cannot be performed, because class C does not implement I1_I2 (only I1 and I2 separately).


One of the possible solutions to this problem could be a class-wrapper

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

but it means that I must implement each method from both interfaces. This solution is unacceptable, because in fact these interfaces (I1, I2) contains a lot more methods.


So my question is : How to create a variable that will allow me to do this :

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

?

Upvotes: 2

Views: 221

Answers (2)

CodeNotFound
CodeNotFound

Reputation: 23220

As you say in your question, you can use a wrapper class but you didn't like it. Another solution is to make the method that use the logic to be a generic method with some constraints.

So let say you have a method that actually look like this:

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

You don't want to allow a call to C.F3() method so a generic method with constraints can help by refactoring the method like below:

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 is a compile time error which tells you this:

CS1061 'I' does not contain a definition for 'F3' and no extension method 'F3' accepting a first argument of type 'I' could be found.

You can call your method like this:

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

We passed an instance of type C which implements I3 with F3() method but because of the constraints defined in MySuperLogic you're not allowed to use F3() because it doesn't exist into I1 and I2.

Upvotes: 2

leofun01
leofun01

Reputation: 235

Perhaps the best solution in this case is

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
}

Source

Upvotes: 0

Related Questions