C# inheritance, abstraction, interface, generic constraints, extension methods

Article directory

  • Preface
  • Simulation requirements
    • Scenario simulation
      • Highly repetitive needs
        • Initial class structure
        • Inheritance optimization
          • abstract class
        • Requirement 1: Print CreateTime
          • Method 1: Use overloading
          • Method 2: Base class function
          • Method 3: Generic constraints
          • Method 3.1: Ordinary generic methods
          • Method 3.2: Advanced generic constraints, extension methods
        • Summarize
      • Extra: Generic methods and interfaces
        • Generic constraint abstract interface
  • Summarize

Foreword

When I was studying computer science as an undergraduate, I knew concepts such as inheritance, overloading, interfaces, abstraction, and generics. This chapter gives priority to the concepts of inheritance, interfaces, abstraction, and generics. If inheritance and abstraction are just to standardize code and standardize program writing, then generic constraints give the necessity of inheriting abstraction.

c# inheritance

A brief discussion of abstract classes and interfaces in C#

C# generic constraints

Simulation requirements

Because I am doing PC work, here I will explain the scenarios I use. The code of the host computer is very repetitive, and generic constraints are used to solve such problems.

Scene simulation

I am company A. I have multiple products (for simplicity, I count them as two). These two products have the following two characteristics:

  • Each product’s fields stored in the database have exactly the same three fields.
    • Id: database ID
    • DeivceId: The unique ID of the device and the unique ID of all types of products.
    • CreateTime: data entry database time

Highly repetitive requirements

We now have a need to add a method for printing CreateTime to each class.

Initial class structure
 public class DeviceA
 {<!-- -->
     public string DeviceId {<!-- --> get; set; }

     public long Id {<!-- --> get; set; }

     public DateTime CreateTime {<!-- --> get; set; }

     public string ValueA {<!-- --> get; set; }
 }

 public class DeviceB
 {<!-- -->

     public string DeviceId {<!-- --> get; set; }

     public long Id {<!-- --> get; set; }

     public DateTime CreateTime {<!-- --> get; set; }

     public string ValueB {<!-- --> get; set; }

 }
Inheritance Optimization

The purpose of inheritance is to reduce duplicate fields

public class DeviceA:DeviceBase
{<!-- -->
    public string ValueA {<!-- --> get; set; }
}

public class DeviceB : DeviceBase
{<!-- -->
    public string ValueB {<!-- --> get; set; }

}
/// <summary>
/// Use base classes to optimize code
/// </summary>
public class DeviceBase
{<!-- -->
    public string DeviceId {<!-- --> get; set; }

    public long Id {<!-- --> get; set; }

    public DateTime CreateTime {<!-- --> get; set; }
}
Abstract class

We can also add the abstract keyword to the base class

public abstract class DeviceBase
{<!-- -->
    public string DeviceId {<!-- --> get; set; }

    public long Id {<!-- --> get; set; }

    public DateTime CreateTime {<!-- --> get; set; }

}

The difference is that abstract classes cannot be instantiated

So the meaning of the abstract class is to tell you that my base class has no practical meaning. I just use it for inheritance.

Requirement 1: Print CreateTime
Method 1: Use overloading
static void Main(string[] args)
{<!-- -->
    var DeviceA = new DeviceA() {<!-- --> CreateTime = DateTime.Now};
    var DeviceB = new DeviceB() {<!-- --> CreateTime = DateTime.Now };
    PrintCreateTime(DeviceA);
    PrintCreateTime(DeviceB);
}
public static void PrintCreateTime(DeviceA deviceA)
{<!-- -->
    Console.WriteLine($"{<!-- -->deviceA.CreateTime}");
}

public static void PrintCreateTime(DeviceB deviceB)
{<!-- -->
    Console.WriteLine($"{<!-- -->deviceB.CreateTime}");
}
Method 2: Base class function
internal class Program
{<!-- -->
    static void Main(string[] args)
    {<!-- -->
        var DeviceA = new DeviceA() {<!-- --> CreateTime = DateTime.Now};
        var DeviceB = new DeviceB() {<!-- --> CreateTime = DateTime.Now };
DeviceA.PrintCreateTime();
DeviceB.PrintCreateTime();
    }
 
}

/// <summary>
/// Use base classes to optimize code
/// </summary>
public class DeviceBase
{<!-- -->
·······
    public void PrintCreateTime()
    {<!-- -->
        Console.WriteLine(CreateTime.ToString());
    }
}
Method 3: Generic constraints
Method 3.1: Ordinary generic methods
 static void Main(string[] args)
        {<!-- -->
            var DeviceA = new DeviceA() {<!-- --> CreateTime = DateTime.Now};
            var DeviceB = new DeviceB() {<!-- --> CreateTime = DateTime.Now };
            PrintCreateTime(DeviceA);
            PrintCreateTime(DeviceB);
        }
        /// <summary>
        /// Generic constraints DeviceBase base class
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        public static void PrintCreateTime<T>(T model)where T : DeviceBase
        {<!-- -->
            Console.WriteLine(model.CreateTime.ToString());
        }
Method 3.2: Advanced generic constraints, extension methods

C# little wheel: extension method

internal class Program
{<!-- -->
    static void Main(string[] args)
    {<!-- -->
        var DeviceA = new DeviceA() {<!-- --> CreateTime = DateTime.Now};
        var DeviceB = new DeviceB() {<!-- --> CreateTime = DateTime.Now };
        DeviceA.PrintCreateTime();
        DeviceB.PrintCreateTime();
    }

}

public static class DeviceExtension
{<!-- -->
    /// <summary>
    /// Extension methods are more advanced generic methods, but require a static class extension
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="model"></param>
    public static void PrintCreateTime<T>(this T model) where T : DeviceBase
    {<!-- -->
        Console.WriteLine(model.CreateTime.ToString());
    }
}
Summary
Advantages Disadvantages
Heavy Loading More flexible, each class can be customized High repeatability
Base class method Reduce duplication High coupling, too many base classes will lead to bloated base classes, which do not conform to the attribute class specifications
General Type constraints Reduce duplication, low coupling, and easy to expand. Cannot access attributes other than the base class
Extension method The strongest scalability, a perfect replacement for the base class Method Requires a separate static class

Attribute class: is my own specification. That is, the basic attribute class only has attributes and constructors, but no methods.

Extra: Generic methods and interfaces

Sometimes the logic of our device class is particularly complex. For example, each product has four methods:

  • connect
  • send
  • accept
  • close connection
public interface IDevice
{<!-- -->
    public void Connect();

    public void Send();
    public void Recive();

    public void Close();

}
public class DeviceA : DeviceBase, IDevice
{<!-- -->
    public string ValueA {<!-- --> get; set; }

    public void Close()
    {<!-- -->
        throw new NotImplementedException();
    }

    public void Connect()
    {<!-- -->
        throw new NotImplementedException();
    }

    public void Recive()
    {<!-- -->
        throw new NotImplementedException();
    }

    public void Send()
    {<!-- -->
        throw new NotImplementedException();
    }
}

public class DeviceB : DeviceBase, IDevice
{<!-- -->
    public string ValueB {<!-- --> get; set; }

    public void Close()
    {<!-- -->
        throw new NotImplementedException();
    }

    public void Connect()
    {<!-- -->
        throw new NotImplementedException();
    }

    public void Recive()
    {<!-- -->
        throw new NotImplementedException();
    }

    public void Send()
    {<!-- -->
        throw new NotImplementedException();
    }
}
Generic constraint abstract interface
static void Main(string[] args)
{<!-- -->
    var DeviceA = new DeviceA() {<!-- --> CreateTime = DateTime.Now};
    var DeviceB = new DeviceB() {<!-- --> CreateTime = DateTime.Now };
    GetData(DeviceA);
    GetData(DeviceB);
}

/// <summary>
/// Generic constraints can also constrain interfaces
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="model"></param>
public static void GetData<T>(T model) where T : IDevice
{<!-- -->
    model.Connect();
    model.Send();
    model.Recive();
    model.Close();
}

Summary

This does not mean that you can only use generic constraints to solve problems, but generic constraints and extension methods are more scalable and have low coupling characteristics. You can use it selectively according to your own coding habits.