Search code examples
c#.netoopmultiple-inheritancegeneric-constraints

How to apply generic constraint to accept multi level of inheritance C#


I am looking for a way to support multi level of inheritance for generic type constraint.

Common generic interface

public interface ICommon<T>
{
    T OrignalData {get;set;}
    string ChangeJson {get;set;}
    T Merged {get;set;}
    void Inject();
}

Common base class implements ICommon

public class Base <T>: ICommon<T>
{
    public T OrignalData {get;private set;}
    public string ChangeJson {get;set;}
    public T Merged {get;private set;}
    public void Inject(T orignal)
    {
        if (orignal == null)
            return;
       
        var settings = new JsonSerializerSettings
        {
            ObjectCreationHandling = ObjectCreationHandling.Auto
        };
        dynamic merged = orignal.Clone();
        JsonConvert.PopulateObject(this.ChangeJson, merged, settings);
        this.Merged  = merged;
        this.Orignal = orignal;
    }
}

Department class inherits Base class

public class Deparment : Base<Deparment>
{
}

OrgnizationDepartment class inherits Deparment

public class OrgnizationDepartment : Deparment
{

}

class View expect ICommon must implement on passed calls

public class View<T> where T : ICommon<T>
{
   //This class is totally dynamic to visualize any json data along with  old and new value of requested json for any class like department or org..
}

Test

public class Test
{
    public void TestConstraint()
    {
        //No error 
        var deptView = new View<Deparment>();

        //Error as Base not directly implemented on OrgnizationDepartment 
        var orgView = new View<OrgnizationDepartment>();
    }
}

how do I define my constraint that should support for multi level as well.


Solution

  • One way out of this would be "composition over inheritance".

    Here is a naive example, that is still relatively close to your code:

    using System;
    using Newtonsoft.Json;
                        
    public class Program
    {
        public static void Main()
        {
            //No error 
            var deptView = new View<Deparment>();
    
            //Formerly Error 
            var orgView = new View<OrgnizationDepartment>();
        }
    }
    
    public interface ICommon<T> where T : ICloneable
    {
        // returns a tuple
        (T,T,string) Inject(T original, string change);
    }
    
    public class Base <T>: ICommon<T> where T : ICloneable
    {
       // we can reuse this...
       private readonly JsonSerializerSettings  settings = new JsonSerializerSettings
            {
                ObjectCreationHandling = ObjectCreationHandling.Auto
            };
    
        public (T,T,string) Inject(T original, string change)
        {
            if (original is null)
                return default;
            
            // this forces T to implement ICloneable ... just saying...
            dynamic merged = original.Clone();
            
            JsonConvert.PopulateObject(change, merged, settings);
            return (original, merged, change);
        }
    }
    
    public class Deparment : ICloneable, ICommon<Deparment>
    {
        // could also be created in ctor. Maybe use Ctor injection.
        private readonly Base<Deparment> common = new Base<Deparment>();
        public object Clone(){return this;} // this is of course nonsense. Clone properly! I did this to avoid dotnetfiddle screaming at me.
        public (Deparment, Deparment, string) Inject(Deparment original, string change){
            return common.Inject(original, change);
        }
    }
    
    public class OrgnizationDepartment : ICloneable, ICommon<OrgnizationDepartment>
    {
        private readonly Base<OrgnizationDepartment> common = new Base<OrgnizationDepartment>();
        public object Clone() {return this;}
        public (OrgnizationDepartment, OrgnizationDepartment, string) Inject(OrgnizationDepartment original, string change){
            return common.Inject(original, change);
        }
    }