C#中类似模板实现的"shim"技术

3

我发现自己需要类似这样的东西:

// This could just as well be a concrete class.
//
// It is an interface for the sake of this example.
public interface Quark
{
    void Do();
}

public class Strange : Quark
{
    public virtual void Do()
    {
        // Something is done
    }

    public void DoSpecificThing()
    {
        MessageBox.Show("Specific Thing!");
    }
}

public class Charm : Quark
{
    public virtual void Do()
    {
        // Something else is done
    }

    public void DoAnotherSpecificThing()
    {
        MessageBox.Show("Another Specific Thing!");
    }
}

在另一个文件中:

internal class ReUsableCode<BaseType> : BaseType // <- not allowed
{
    public override void Do()
    {
        // Let's pretend this is around 2000 lines of
        // 90's-style Win32 message spaghetti.

        MessageBox.Show(base.GetType().ToString() + " did!");

        base.Do();
    }
}

public class LibraryStrange : ReUsableCode<Strange>
{
}

public class LibraryCharm : ReUsableCode<Charm>
{
}

在某个函数中:

LibraryStrange imStrange = new LibraryStrange();
LibraryCharm imCharmed = new LibraryCharm();

imStrange.Do();
imStrange.DoSpecificThing();

imCharmed.Do();
imCharmed.DoAnotherSpecificThing();

在C++中,我只需要创建一个模板就可以实现上述功能。

由于C#不支持多重继承,并且出于上述原因,这在C#中是不可能实现的。那么,如何在不复制和粘贴或强制所有内容继承自单个基类的情况下重用词汇上相同的实现呢?

这旨在减少库中所有用户控件所继承的System.Windows.Forms对象并以完全相同的方式重写WndProc的维护工作量(代码都复制粘贴了,我正在尝试消除或集中它们)。

注意:我的C#工作量不是很大,所以如果这个问题看起来很基础或者滥用了这个领域的术语,请谅解。


我更新了我的问题以澄清问题的本质。这里所说的“基本类型”不可避免地具有广泛变化的实现,并且可能与shim所使用的任何内容没有任何共同之处。 - defube
再次更新。问题在于,由于所有涉及的内容都以某种方式派生自“Control”,否则我将需要重复(大量次数)完全相同的“WndProc”覆盖。项目中还有一些涉及不在“System.Windows.Forms”内的其他类型的类似问题。 - defube
2个回答

2
你可以使用扩展方法来实现这个功能。
public static class ExtensionMethods {

    public static void Do(this ICommon obj, String someArg) {
        Console.WriteLine( obj.GetType().Name +  " foobar " + someArg );
    }
}
public interface ICommon {} // interface doesn't do anything

public class Strange : ICommon {

}
public class Charm : ICommon {

}

public static class Program {

    public static void Main(String[] args) {

        Strange quark = new Strange();
        Charm rom = new Charm();
        quark.Do("blargh");
        rom.Do("blargh");
    }
}

谢谢您的答案。我会尝试一下,虽然我想要的效果是消除所有地方的 override void WndProc 等等。 - defube
如果实现方式相同,您可以将其移动到新方法中,然后从不同独立的“WndProc”方法中调用它。 - Dai
我认为在C++领域中,继承被高度误用。聚合/组合更加灵活和强大。所以我的意见是,除了扩展方法,您还应该考虑创建一个Doer类,该类具有类似于DoTheDoing(ICommon toDo)的方法。看看这个讨论反对继承的理由... https://dev59.com/8nRC5IYBdhLWcg3wG9To - Mick
@Mick:我完全同意你在继承方面的链接。然而,当涉及到像C#中的事件这样的东西时,据我所知,它们只能由包含它们的对象调用,使用简单的继承来“插入”重复的覆盖将节省大量时间。此外,我的具体问题不需要多重继承——重新考虑一下,如果首先允许多重继承,我认为它不会按照我的预期工作... - defube

2
我想到的最简单的解决方案是放弃继承,转而采用组合。不幸的是,它需要概括“SpecificThing”的概念。
问题正是因为你的设计违反了SRPISP,你所想出的一切都只是为了避免这个问题而做的 hack,而不是真正解决它!
你在评论中写道:“所涉及的“基本类型”[...]”,把它放在引号中是正确的——这些不应该是基本类型。然而,表述过于笼统,很难明确指出问题所在——这里似乎一切都错了。
public interface Quark
{
    void Do();
}

public interface SpecificQuark : Quark
{
    void DoSpecificThing();
}

public class Strange : SpecificQuark
{
    public virtual void Do()
    {
        // Something is done
    }

    public void DoSpecificThing()
    {
        Console.WriteLine("Specific Thing!");
    }
}

public class Charm : SpecificQuark
{
    public virtual void Do()
    {
        // Something else is done
    }

    public void DoSpecificThing()
    {
        Console.WriteLine("Another Specific Thing!");
    }
}

class ReUsableCode<T>
    where T: SpecificQuark, new()
{
    public T InnerQuark { get; private set; }

    public ReUsableCode()
    {
        this.InnerQuark = new T();
    }

    public void Do()
    {
        // Let's pretend this is around 2000 lines of
        // 90's-style Win32 message spaghetti.

        Console.WriteLine(InnerQuark.GetType().ToString() + " did!");
        this.InnerQuark.Do();
    }
}

class LibraryStrange : ReUsableCode<Strange>
{
}

class LibraryCharm : ReUsableCode<Charm>
{
}

然后:
LibraryStrange ls = new LibraryStrange();
LibraryCharm lc = new LibraryCharm();

ls.Do();
ls.InnerQuark.DoSpecificThing();
lc.Do();
lc.InnerQuark.DoSpecificThing();

再次感谢!如果我作为一个新项目的顾问,像这样的自上而下的接口方法会很好地工作。不幸的是,我要处理的是一个庞大、复杂、难以维护的项目,所有的控件都必须能够替代Forms命名空间中的基本类型。这个项目正是为什么好的设计一次性花费很多时间和金钱,而糟糕的设计在许多场合上花费更多倍数的另一个例子。 - defube

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