Reputation: 2433
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.
Upvotes: 3
Views: 212
Reputation: 35891
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();
Upvotes: 2
Reputation: 154995
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");
}
}
Upvotes: 2