amnezjak
amnezjak

Reputation: 2031

Generic wrapper class

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

Answers (4)

derabbink
derabbink

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

quetzalcoatl
quetzalcoatl

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

Sergey Kalinichenko
Sergey Kalinichenko

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

Jon Skeet
Jon Skeet

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

Related Questions