0

I could also ask this question for Java, D or C++. I remember, I found this to be an artificial limitation in Java already 10 years ago. I am currently working with C# and appreciate advices here.

It's easy to share a common interface between classes but I find the story to be different for implementation patterns and code contracts.

Sometimes I find the need to share implementation patterns of class-code (not instance code!) between subclasses and sibling classes which vary dependent on the class.

This issue occurs always when working with higher-order concepts, code that is used to define how classes behave to each other, not instances. A common case is multitons (Java-like enums) or "multiton-classes" (the OOP-version of Algebraic Datatypes or Sumtypes). These may be used to define design rules for class members or class code or define a cohesive relation when a type's logic is split into multiple case classes but which appear in a common class externally. These case classes show repeated code patterns of class methods and class implementation parts which slightly differ between sibling classes.

Polymorphy is the OOP-way to reuse cohesive code.

Clones of instance code of related classes are refactored using polymorphy: a new base class is created from which the common code is distributed to subclasses. However, the only ways in C#, that I know to share and refactor methods and members on a per-class level have multiple problems because the features and techniques of working with instances is not available when working with classes.

Therefore, in order to remove cohesive clode clones (which change together but differ in some implementation details), I need to replace classes with singletons, static members are replaced by instance members, i.e. substituting either full (abstract or static) classes with singletons where the singleton contains a Factory method to simulate a constructor.

The other way is to substitute only static members with a dedicated external singleton so that every subclass needs to provide a 2nd subclass in order to override conceptual class code (which now is simulated with instance code). If features grow or requirements change, at an earlier or later time, it happens to most static members that they change to instance members (inherited or composed), once the superior capabilities of instance members are needed on a class-level to fight code clones.

Disadvantages with the singletons are

  1. The amount of work for manually refactoring class hierarchy bases into singletons can be enormous, if the entire class should be refactored (all subclasses need to be refactored to singletons because static methods are gone!). The output of the factory also requires the old instance members to be turned into a new class which can be constructed.

  2. On the other hand, the disadvantage of refactoring only static members into an own class is, it doubles the number of explicit classes in the hierarchy and requires new classes to override and inherit the base's singleton class.

EDIT Composition, as suggested in the comments, is not suitable for sharing cohesive code patterns. Composition is a static alternative to inheritance, like "inheritance without the possibility to override or implicitly use the members of the composed member". Composition makes sense if the composed member needs to be decoupled/separated from the class (when they don't change together). How much composition you need depends on how generically code is written. Composition prevents changes from one side to appear on the other side and can have higher maintenance costs but in return allow for dependency injection. EDIT END

The other way of sharing implementations is to use global extension methods (the functional approach), which however have the same problem that they are static. If we have multiple static extension classes, and it appears that code patterns are cloned from one to the other or between methods, the classes need to be refactored to avoid code clones. Static classes cannot be instantiated or inherited. It severely limits their usefulness to share common implementation patterns in their class structure. They behave more like a namespace than a class.

There are two ways to reuse code from extension methods for different class instances:

  • either factoring out class-specific implementation differences into virtual or interface methods (which C# prevents by default!), and overriding them in a (sub)class
  • if the code variation is only related to the extension method itself (not the argument types) and slightly different extension method versions are needed for new classes, then the variation of code needs these extension methods to be refactored into an inheritable abstract base class which is inserted somewhere in the hierarchy (that one hopefully has control of) – instead of invoking static methods.

It's worse if the extension method implementation is specific for specific argument types and needs to consider specific details of classes or class hierarchies. Then, if we collect these extensions methods in one extension class, the different method implementations may be assembled in the same static class (even though they contain class-specific details). And the method implementations would need to use details of the argument types which do not belong into the same static class but which would belong into the classes of the argument types.

We can use interfaces between multiple extension classes but interfaces don't allow sharing and refining common implementation patterns for the methods. (I actually don't understand, why extension methods may not have dynamic dispatch in C#.)

If extension methods for specific subclasses are provided, explicit casting (or the convenient dynamic type) is required to invoke them because dynamic dispatch is not available. This explicit casting can lead to a manual reimplementation of dynamic dispatch using pattern matching, switch-case statements.

The essence of my problem is, with singletons or extension methods, it creates a lot of work and refactoring to share class implementation patterns between subclasses or siblings. It becomes complex to read, understand, to extend and the amount of refactoring is annoying which discourages polymorphic code reuse.

I am asking if C# provides a better way to achieve overriding/inheritance of per-class members instead of turning static members or abstract base classes into singletons for the purpose of sharing code between multi-class structures such as multitons, sumtypes (tagged Unions) or algebraic datatypes.

I believe, singletons are only ever needed to compensate for a lack of features to handle cross cutting concerns (such as Aspects) or to handle higher-order code (between class members or class structures).

Python on the other hand has per-class members, distinguished from static methods and doesn't have this problem. Meta programming actually solves this problem, using Python metatypes to describe mutable code between classes instead of instances.

Prototype-based languages replace immutable classes with mutable prototypes, sometimes allowing for mutating a whole inheritance hierarchy at runtime, they don't have the problem.

What is the best technique in C# to conveniently reuse slightly varying class code (not instance code) between classes?

8
  • 4
    you've written quite a long post with lots of technical terms but no examples. It gives the impression you are doing something unusual which has lead to your problem and you don't want to be told "well just do it normally!" Can you give an example of your multion structure and highlight the issue with static members?
    – Ewan
    Commented Jul 7 at 18:51
  • 1
    If I understand this correctly, you essentially you want multiple inheritance? Commented Jul 7 at 21:07
  • 2
    Can you clarify how "Therefore, in order to simulate the code-reuse (polymorphy) of static members" has anything to do with polymorphism? Polymorphism is achieved with instance members, not static. Commented Jul 7 at 23:39
  • 1
    I would suggest if you want to reuse some code which happens to belong to a class containing other behaviour that you don't want, then it's a strong indicator that the code you want to reuse should be split out away from that class entirely. I would very strongly discourage inheritance for code reuse; inheritance is fundamentally about extension of existing behaviour (it is generally referred to as an "IS-A" relationship), it isn't about cherry-picking behaviour to create completely new classes. Code reuse is often better handled through composition instead. Commented Jul 8 at 6:49
  • 1
    What about per instance members, and creating singletons of those types? Commented Jul 10 at 5:33

0