通用包装类

10

给定以下层次结构:

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

这是一个第三方库,我无法修改它。是否有一种方式可以编写某种“通用模板包装器”,将Foo()方法转发到构造函数参数中传递的适当对象? 最终我编写了以下代码,没有使用泛型,看起来相当丑陋:
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(); }
        }

    }

我希望有一些类似于 Wrapper<T> where T : B or C 的模板限制。


1
我认为主要问题在于A中的Foo没有声明为virtual,是吗? - Sergey Kalinichenko
4个回答

15

如果 A 没有 Foo,你需要使用 dynamic(参见 Jon Skeet的回答),或者利用lambda表达式和重载的小技巧:

class Wrapper {
    private Action foo;
    public Wrapper(B b) {
        foo = () => b.Foo();
    }
    public Wrapper(C c) {
        foo = () => c.Foo();
    }
    public void Foo() {
        foo();
    }
}

现在你可以这样做:

var wb = new Wrapper(new B());
wb.Foo(); // Call B's Foo()
var wc = new Wrapper(new C());
wc.Foo(); // Call C's Foo()

这会将决定调用哪种方法的时机从调用Foo时刻转移到创建Wrapper时刻,可能节省一些CPU周期。


谢谢大家!我认为我会采用你们的解决方案,因为还有更多类似Foo的函数,但由于篇幅原因我省略了它们。我还需要在我的代码中多次使用包装器,而动态似乎太内联了。 - amnezjak

8
不,就编译器而言,这两个“Foo”方法是完全无关的。不需要事先了解各个类型,最简单的方法是使用动态类型:
public void Foo()
{
    dynamic d = a;
    // Let's hope there's a suitable method at execution time!
    d.Foo();
}

据我所知,泛型在这里不会有帮助。你没有展示出可以限制T的接口。

你也可以传递一个Action

Wrapper wrapper = new Wrapper(b, b.Foo);

这使得呼叫者的使用略显不便,但更具通用性...

1
这是 Skeet paunch。你真棒,Skeet。 - JSJ
将Jon的“action passing”与dasblinkenlight的建议以及一个T4模板合并,你应该得到一个非常合理且易于维护的解决方案。 - quetzalcoatl

0

虽然我不太情愿建议这样做,但既然您无法修改库... 如果性能不是关键问题,可以考虑使用 dynamic 关键字 :)

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();
    }
}

编辑:在其他情况下,我个人会像dasblinkenlight所展示的那样使用lambda表达式来进行编译时检查。它可以很容易地自动生成,例如使用T4s或任何其他文本生成器。


0

你可以创建一个平行的层次结构,该结构包含根级别的Foo()方法。
使用工厂方法,你可以为任一类型创建一个Wrapper实例。为了使其正常工作,你需要在调用工厂方法时知道你的A实例的确切类型。

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();
    }
}

编辑:这基本上与此答案相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接