3

I've found myself in need of something like this:

// 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!");
    }
}

In another file:

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>
{
}

In a function somewhere:

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

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

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

In C++, I'd just make a template that does precisely the above.

This isn't possible in C#, for the reason above, and that multiple-inheritance is also disallowed. So, how might one re-use lexically identical implementations without copying and pasting, or forcing everything to inherit from a single base class?

This is to reduce the maintenance effort required for a library of user controls that all inherit from things in System.Windows.Forms and also override WndProc in exactly the same way (the code is all copied and pasted, and I'm trying to eliminate or centralize it).

Note: I don't do a substantial amount of C# work, so forgive me if this question seems elementary or abuses the terminology in this realm.

defube
  • 2,395
  • 1
  • 22
  • 34
  • Updated my question to clarify the nature of the problem. The "base types" in question here have unavoidably widely-varied implementations, and might not have anything in common with whatever the shim might be used with. – defube Nov 27 '13 at 00:31
  • Updated again. The problem is that, because everything involved derives in some way from `Control`, I would otherwise need to repeat (a large number of times) the exact same override of `WndProc`. There are also areas of the project that have similar problems involving other types not within `System.Windows.Forms`. – defube Nov 27 '13 at 00:55

2 Answers2

2

You can sort-of do this with Extension Methods.

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");
    }
}
Dai
  • 141,631
  • 28
  • 261
  • 374
  • Thank you for the answer. I'll give this a try, though the effect I was looking for was the elimination of all the `override void WndProc` etcetera all over the place. – defube Nov 27 '13 at 00:26
  • If the implementation is the same, you could just move it to a new method and then call it from the different independent `WndProc` methods. – Dai Nov 27 '13 at 00:33
  • I think inheritance is highly misused in the world of C++. Aggregation/Composition is much more flexible and powerful. So my 2c is that apart from extension methods you should also consider creating a Doer class that has a method like DoTheDoing(ICommon toDo) See this discussion for arguments against inheritance.... http://stackoverflow.com/questions/406081/why-should-i-avoid-multiple-inheritance-in-c – Mick Nov 27 '13 at 00:49
  • @Mick: I completely agree with what you've linked on inheritance. However, when it comes to things like events in C#, which (as far as I know) can only be invoked by their containing object, being able to "insert" duplicated overrides using straightforward inheritance would be a real time saver. Also, my specific problem does not require multiple inheritance -- and thinking about it again, I don't believe it would have worked as I expected if it were allowed in the first place... – defube Nov 27 '13 at 01:02
2

The simplest solution I've come up with is to give up inheritance in favor of composition. Unfortunately it needs to generalize the "SpecificThing" concept.

The problem lies exactly in the fact that SRP and ISP are being violated in your design and everything you'll come up with will be just hacks to avoid the problem instead of solving it!

You write in the comments "The "base types" in question [...]" - and it's good to put that in quotes - these shouldn't be base types. The formulation is too general however and it's hard to point out explicitly what is wrong - everything seems wrong here.

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>
{
}

And then:

LibraryStrange ls = new LibraryStrange();
LibraryCharm lc = new LibraryCharm();

ls.Do();
ls.InnerQuark.DoSpecificThing();
lc.Do();
lc.InnerQuark.DoSpecificThing();
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
  • Thanks again! If I were consulting on this as a new project, a top-down interface approach like this would work nicely. Unfortunately, what I have to work with is already a big, fat, hairy abomination of a project, and all the controls had to be able to substitute as their base types in the `Forms` namespace. The project in question is just another example of why good designs cost a lot of time and money once, and bad designs cost many times more on many occasions. – defube Nov 28 '13 at 00:57