34

Suppose I have the following class hierarchy:

Class A {...}

Class B : A  {...}

Class C : A {...}

What I currently have is

Class D<T> where T : A {...}

but I'd like something of the form

Class D<T> where T in {B,C}

This is due to some odd behavior I'm not responsible for where B and C have common methods which aren't in A, but it would be nice to be able to call them in D on T.

Note: I don't have access to A,B or C to edit them

Jean-Bernard Pellerin
  • 12,556
  • 10
  • 57
  • 79

7 Answers7

27

You need to define an interface for the common methods that are in B and C (lets call it Ibc), make B and C implement this interface, and then you can write:

Class D<T> where T : A, Ibc {...}
Grzenio
  • 35,875
  • 47
  • 158
  • 240
  • 12
    But, he does not control the sources A, B and C. Will make this solution a bit hard to implement. – GvS Jun 21 '10 at 15:33
  • Ah I haven't seen the edit. If such an interface doesn't exist, then there is no direct way to implement class D as far as I know. – Grzenio Jun 21 '10 at 15:50
  • 1
    +1 - This is the preferred solution. I added an answer that say how it can be done in *some* cases. It is curious you got the accepted answer though... @Jean-Bernard Pellerin - can you please explain how you solved it? – Kobi Jun 21 '10 at 16:04
  • 2
    Actually in the end I didn't use either of your answers. I added an extension method to A which was overwritten by the real ones :) – Jean-Bernard Pellerin Aug 26 '10 at 22:48
11

First, If B and C have common methods, it is a design flaw they don't share an interface. That said, you can fix that even without having access to B and C.
It is possible to create a common interface. Suppose you have:

public class A
{
}
public class B : A
{
    public void Start() { }
}
public class C : A
{
    public void Start() { }
}

You can create a common interface:

public interface IStartable
{
    void Start();
}

And use it on derived classes from B and C:

public class BetterB : B, IStartable
{
}
public class BetterC : C, IStartable
{
}

You may not be able to achieve that if you get B and C instances as is, but it can be considered if you create them. In fact, with specialized classes of B and C, you may use the interface instead of D<T>.

Kobi
  • 135,331
  • 41
  • 252
  • 292
  • I like this answer. It also provides a more generic solution where there is no A, e.g. if B and C come from libraries with different sources – Paul Childs May 06 '21 at 01:22
9

This isn't directly possible.

As others suggest, you could define an interface and implement it in both B and C.

If this isn't an option (e.g., if these classes are beyond your control), what I might suggest is this: first, start with an abstract class that includes all the functionality you can achieve with any T deriving from A. Then say you have some methods that exist for both B and C that aren't a part of A. In D you can make these abstract methods to be implemented by subclasses:

public abstract class D<T> where T : A
{
    protected T _member;

    public void DoSomethingAllTsCanDo()
    {
        _member.DoSomething();
    }

    public abstract void DoSomethingOnlyBAndCCanDo();
}

Then you can inherit from the base class for each type B and C and override the abstract method(s) to provide the appropriate functionality:

public class DB : D<B>
{
    public override void DoSomethingOnlyBAndCCanDo()
    {
        _member.DoSomethingOnlyBCanDo();
    }
}

public class DC : D<C>
{
    public override void DoSomethingOnlyBAndCCanDo()
    {
        _member.DoSomethingOnlyCCanDo();
    }
}
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
3

Do B and C implement the same interface? That may be a better route.

Anthony Potts
  • 8,842
  • 8
  • 41
  • 56
2

Some options:

  1. Make an interface IderivedFromA that contain the common methods from B and C.
    Looks like this is impossible from your question
  2. In D cast T to dynamic and call the methods dynamically
    The most easy solution, if you can use .Net 4
  3. In D test if the you deal with an B or C, cast, and call
    Will be checked by the compiler, and is possible from .Net 2
  4. The Dan Tao answer: Create a specific implementation of D<T> for B and C, these can call the methods from B and C directly. (Didn't think of this one myself).
    Will only work if the "user-source" knows it is dealing with B or C, and does not use the abstract A to use D<A>. Instead it should use DB or DC. But I think this is the case, otherwise you didn't need generics.
Community
  • 1
  • 1
GvS
  • 52,015
  • 16
  • 101
  • 139
1

The where constrain in C# does not allow you to specify multiple classes as a choice. Also if you will specify multiple where contains, then they both has to be satisfied. There is no OR logic for constrain. Here is specification: http://msdn.microsoft.com/en-us/library/bb384067.aspx

Answers from Grzenio seems right for you. Extract common behavior into the common interface for B and C. Then you can use that interface as a constrain.

Dennis
  • 2,615
  • 2
  • 19
  • 20
0

Since you don't have access to the source, the only real answer (unless you are willing to lose safety by using dynamic) is explicitly check for B/C and cast.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283