1

I know that multiple inheritance in C# is only allowed by using Interfaces and that there are very valid reasons why multiple inheritance can quickly become a real headache. (Working in .NET Framework if that makes any difference to the answers)

However.

In working on various projects accross many classes I find myself returning to the same patterns to handle behaviour. For example I have an Interface IXMLSavable which requires the functions GetXML() and SetFromXML(XElement e) to be implemented. The way I implement this in every class is, that I write different functions for different versions of the XML (If I changed something in the GetXML() I want to maintain backwards compatibility...). And according to a version-attribute on the root Element I switch case to the right ReadVersionX(XElement e) function so all my data stays consitent.

Another example would be centered around eventing. If for example I want to implement a "stop firing events for the time being"-Lock I would go about thusly:

private bool suppressEvents;
public bool SuppressEvents
{
    get { return suppressEvents; }
    set
    {
        bool prevValue=SuppressEvents;
        suppressEvents=value;
        if(prevValue!=SuppressEvents && !SuppressEvents) TheChangeEvent?.Invoke();
    }
}

So I can run multiple operations on the object in question without it giving of a right old firework display of events. Again: This code will be almost unchanged for a lot of classes.

For the XML one I could refactor this to a class that has a Dictionary<int,delegate> ReadFunctions which I could then set in every implementation (I concede that there needs to be a bit of customisation in the "implementing" class) and reduce the amount of bolierplate for every class (the explicit switching on the version attribute) to just filling this dictionary.

The eventing one could go into a class on its own quite readily, I would probably only need to hook up the right event to the invokation, but that could easily be remedied by an abstract function I will have to implement (again: customisation still necessary but much less boilerplate).

Each "now-class-was-interface" on its own would make a splendid base class for any object. I could use functionality down an inheritance tree and customise it by overwriting functionality with new if I would need it.

The problem starts when I want to combine the two now-classes together. Due to the limitation in C# (which, again, is there for a reason) I cannot inherit from both above described classes at the same time. This would only be possible if I have one of these classes inherit from the other. Which would be possible, but would lead to a whole lot of a different headache when I want one functionality but not the other. Or the other functionality and not the one. The way around that would be to create a plethora of permutation classes (so one class for each combination of the functionaities). And while that would solve the problem it would probably be a nightmare to maintain.

So the real question is: Is there a way to correctly plug in different already implemented functionality into a class in an inheritance like manner that allows the addition of multiple distinct functionality packages as opposed to interfaces that cannot by the very nature of themselves provide any concrete implementation.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
lhiapgpeonk
  • 457
  • 1
  • 5
  • 18
  • 1
    Since C# version 8 there is support for default implementation in interfaces. Hence, you _can_ you interfaces to achieve quasi-multiple inheritance, combining several pre-defined functionalities in a single class. See also https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods. – Ondrej Tucny Nov 28 '22 at 09:01
  • Unfortunately it is not possible with .NET Framework https://stackoverflow.com/a/57020770/1968308 – lhiapgpeonk Nov 28 '22 at 09:07
  • [Traits in C# using Fody](https://blog.adamfurmanek.pl/2016/10/22/traits-in-c-part-1/#:~:text=Trait%20is%20something%20between%20class,like%20method%20in%20a%20class). – Robert Harvey Nov 28 '22 at 09:13
  • There are a *lot* of ways to plug in functionality. Inheritance is probably the worst in all languages. There are generics, extension methods, attributes, adapters, wrappers, middleware etc. Since C# 8 there are also [Default Interface Methods](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods) that can be used to implement traits. Java uses the same concept. This allows eg the Android SDK to change from one Android version to another without breaking existing applications – Panagiotis Kanavos Nov 28 '22 at 09:23
  • Regarding `IXMLSavable`, don't. Having a serializable type is one thing. Hard-coding a specific format, especially one that's out of fashion for the last 10 years, isn't a good idea. Especially when the preferred formats for event streaming are JSON, Protobuf, Thrift etc. Every service encounters the same problems you do, but none had to use multiple inheritance to solve them. – Panagiotis Kanavos Nov 28 '22 at 09:27
  • 1
    @PanagiotisKanavos: Just because it may be out of fashion in certain domains does not mean it might not help solve a problem without much hassle. And event streaming is not a problem I am trying to solve. It is rather used for data storage, and while it may not be the best data format there is, it works for my purpose as intended. – lhiapgpeonk Nov 28 '22 at 09:35
  • @PanagiotisKanavos RE: your previous answer: Most of the concepts you mention do ring one or two bells. However: currently I cannot see any benefits in regard of having less boilerplate by employing any of them. Care to expand on that in an answer? And for the time being switching from .NET Framework (which does not support default instance methods) to C# 8 is no viable option. – lhiapgpeonk Nov 28 '22 at 09:37
  • 1
    The point is that serialization should never be baked into entities or DTOs. What if the schema changes? .NET Framework already supports customizable XML serialization through its own XML attributes and classes like XmlSerializer and DataContractSerializer. Why not use these? The question then becomes `how does XmlSerializer handle versioning` or `how does DataContractSerializer handle versioning?`. There are a lot of articles that explain how to do that, eg [Data Contract Versioning](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-versioning) – Panagiotis Kanavos Nov 28 '22 at 09:46
  • @PanagiotisKanavos I thank you for this insight, I will give it a read! – lhiapgpeonk Nov 28 '22 at 10:09

1 Answers1

1

In many cases you can avoid inheritance with the use of interfaces/default interface methods/extension methods, decorators, or some other pattern.

In your case with xml you could simply change your interface to have one ReadMethod per version, and use a extension method to select the correct one

public interface IXMLReadable{
    void ReadVersion1(XElement e);
    void ReadVersion2(XElement e);
}
public static class IXMLReadableExtensions {
    public static void Read(this IXMLReadable self, XElement e){
        // Read version from xml, call ReadVersion1 or ReadVersion2
    }
}

default interface methods would do more or less the same thing, with the added advantage of allowing the class to override the Read-method if it wants some other behavior.

However, my preferred solution would be to instead convert your object to a Data Transfer Object (DTO), add any required serialization attributes to this object, and use a library to serialize this. Added fields etc can usually be accommodated by just marking it as optional. Larger changes can usually be done by creating a new DTO class.

One way to solve your event problem could be to move this logic to a separate class

public class SuppressibleEvent
{
    private bool suppressEvents;
    private bool pendingEvent;
    public void Raise()
    {
        if (!suppressEvents)
        {
            TheChangeEvent?.Invoke(this, EventArgs.Empty);
        }
        else
        {
            pendingEvent = true;
        }
    }

    public event EventHandler TheChangeEvent;
    public bool SuppressEvents
    {
        get => suppressEvents;
        set
        {
            suppressEvents = value;
            if (!suppressEvents && pendingEvent)
            {
                TheChangeEvent?.Invoke(this, EventArgs.Empty);
                pendingEvent = false;
            }
        }
    }
}

Optionally you may add a interface, so that only the owner can raise the event, but others can listen and register. You could also add methods/events to your class that just forwards to the actual implementation.

The overall point is that there is usually a better pattern to use than implementation inheritance. Some might require a bit more code, but usually gain a bit of flexibility as a result.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • Thank you for this! I will definitely think about the Interface one. The example with the class for the event is along the lines of what I would do. The only problem there would be if I had a similarly constructed class for a completely unrelated bit of functionality that I would also like to add to a class, hence the question. – lhiapgpeonk Nov 28 '22 at 10:14