Reputation: 2031
Given the following hierarchy:
class A
{
}
class B : A
{
public void Foo() { }
}
class C : A
{
public void Foo() { }
}
This is a third-party library and I can't modify it. Is there a way I can write some kind of 'generic templated wrapper' which would forward the Foo() method to the apropriate object passed as constructor argument? I ended up writing the following, which uses no generics and seems rather ugly:
class Wrapper
{
A a;
public Wrapper(A a)
{
this.a = a;
}
public void Foo()
{
if (a is B) { (a as B).Foo(); }
if (a is C) { (a as C).Foo(); }
}
}
I'd love some template constraint like Wrapper<T> where T : B or C
.
Upvotes: 10
Views: 14683
Reputation: 2429
You can create a parallel hierarchy that does contain the Foo()
method at the root level.
Using factory methods you can create a Wrapper instance for either type. For this to work you need to know the exact type of your A
instance when you call the factory method
abstract class Wrapper {
public abstract void Foo();
//factory methods
public Wrapper FromB(B instance) {
return new WrapperB(instance);
}
public Wrapper FromC(C instance) {
return new WrapperB(instance);
}
}
class WrapperB {
private B instance {get; set;}
public WrapperB(B instance) {
this.instance = instance;
}
public void Foo() {
instance.Foo();
}
}
class WrapperC {
private C instance {get; set;}
public WrapperC(C instance) {
this.instance = instance;
}
public void Foo() {
instance.Foo();
}
}
Edit: this is basically the same as this answer
Upvotes: 0
Reputation: 33536
I'm reluctant to suggest it, but as you can not modify the library.. If this is not performance-critical, recall the dynamic
keyword :)
class Wrapper
{
public dynamic theAorBorC;
public Wrapper(A a){theAorBorC=a;}
public Wrapper(B b){theAorBorC=b;}
public Wrapper(C c){theAorBorC=c;}
// or even...
// public Wrapper(object anything){theAorBorC=anything;}
public void CallFoo()
{
theAorBorC.Foo();
}
}
Edit: in all other cases, I'd personally use lambdas similarly to what dasblinkenlight has shown - to get compile-time checking. It can be easily autogenerated i.e. with T4s or any other text generator.
Upvotes: 0
Reputation: 726599
If A
does not have Foo
, you need to either use dynamic
(see Jon Skeet's answer) or use a little trick with lambdas and overloading:
class Wrapper {
private Action foo;
public Wrapper(B b) {
foo = () => b.Foo();
}
public Wrapper(C c) {
foo = () => c.Foo();
}
public void Foo() {
foo();
}
}
Now you can do this:
var wb = new Wrapper(new B());
wb.Foo(); // Call B's Foo()
var wc = new Wrapper(new C());
wc.Foo(); // Call C's Foo()
This shifts the decision on what method to call from the moment the Foo
is called to the moment the Wrapper
is created, potentially saving you some CPU cycles.
Upvotes: 15
Reputation: 1500725
No, the two Foo
methods are completely unrelated as far as the compiler is concerned. The simplest way of doing this without knowing about the individual types to start with would be to use dynamic typing:
public void Foo()
{
dynamic d = a;
// Let's hope there's a suitable method at execution time!
d.Foo();
}
Generics won't help you here, as far as I can tell. It's not like there's some interface (at least none that you've shown) which you can constrain T
to.
You could pass in an Action
as well:
Wrapper wrapper = new Wrapper(b, b.Foo);
which makes it slightly less convenient for the caller, but very general...
Upvotes: 8