Абстрактный класс и Interface в C#

1- Введение

В этой статье я расскажу вам об интерфейсе и абстрактном классе (Abstract Class), а также проанализирую сходства и различия между ними.
Для начала создайте проект с названием  AbstractClassInterface для работы с примерами.
Настройте его как проект по умолчанию.

2- Абстрактный класс (Abstract Class)

Абстрактный класс. Посмотрим пример про такой класс:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{
   // A class has at least one abstract method,
   // must be declared as abstract.
   public abstract class ClassA
   {

       // This is an abstract method.
       // It has no body.
       // The 'access modifier' of this method is public
       public abstract void DoSomething();

       // The 'access modifier' of this method is protected.       
       protected abstract String DoNothing();

      
       protected abstract void Todo();
   }

   // This is an abstract class.
   // It is declared as abstract, although it does not have any abstract methods.   
   public abstract class ClassB
   {

   }
}
Характеристики абстракного класса:
  1. Объявлен с ключевым словом abstract.
  2. Он может объявлять 0, 1 или более абстракных методов внутри.
  3. Вы не можете инициализировать один объект прямо из абстрактного класса.

3- Пример с абстрактным классом

Смотрите иллюстрацию:
AbstractJob.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{
    // Класс имеет минимум 1 абстрактный метод,
    // должен быть объявлен абстрактным (abstract).
    public abstract class AbstractJob
    {

        public AbstractJob()
        {

        }

        // Это абстрактный метод,
        // Он не имеет тела (body).
        // Данный метод возвращает название работы.
        public abstract String GetJobName();

        // Это абстрактный метод,
        // Метод не имеет тела (body).
        public abstract void DoJob();

        // Это обычный метод (Неабстрактный).
        public void StopJob()
        {
            Console.WriteLine("Stop");
        }
    }
}
JavaCoding.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{
    public class JavaCoding : AbstractJob
    {

        public JavaCoding()
        {
        }

        // Выполняет абстрактный метод объявленный в родительском классе.
        // Он не имеет тела (body).
        // (Должно быть ключевое слово 'override').
        public override void DoJob()
        {
            Console.WriteLine("Coding Java...");
        }

        // Выполняет абстрактный метод родительского класса.
        public override String GetJobName()
        {
            return "Java Coding";
        }

        public void TestExample()
        {
            Console.WriteLine("Testing Example...");
        }
    }

}
CSharpCoding.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{
    class CSharpCoding : AbstractJob
    {
        public CSharpCoding()
        {
        }

        // Выполняет абстрактный метод объявленный в родительском классе.
        // Должно иметь тело (body).
        // (Должно иметь ключевое слово 'override').
        public override void DoJob()
        {
            Console.WriteLine("Coding CSharp...");
        }

        // Выполняет абстрактный метод родительского класса.
        public override String GetJobName()
        {
            return "CSharp Coding";
        }

        public void RunningExample()
        {
            Console.WriteLine("Running Example...");
        }
    }
}
ManualJob.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // ManualJob - (Симулирует обычную работу)
    // Его родительский класс (AbstractJob) имеет 2 абстрактных метода.
    // Данный класс только выполнил 1 метод родительского класса.
    // Поэтому он должен быть объявлен абстрактным 'abstract'.
    public abstract class ManualJob : AbstractJob
    {

        public ManualJob()
        {

        }

        // Выполнить абстрактный метод объявленный в родительком классе.
        // (Должно быть ключевое слово 'override').
        public override String GetJobName()
        {
            return "Manual Job";
        }

    }
}
BuildHouse.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // Данный класс унаследован от абстрактного класса ManualJob.
    // BuildHouse не объявляет 'abstract'
    // Поэтому ему нужно выполнить все остальные абстрактные методы.
    public class BuildHouse : ManualJob
    {

        public BuildHouse()
        {

        }

        // Развернуть абстрактный метод родительского класса.  
        // (Нужно использовать ключевое слово 'override').
        public override void DoJob()
        {
            Console.WriteLine("Build a House");
        }

    }

}
Demo
JobDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{
    public class JobDemo
    {

        public static void Main(string[] args)
        {
            // Создать объект AbstractJob из Constructor класса JavaCoding.
            AbstractJob job1 = new JavaCoding();

            // Вызвать метод DoJob().
            job1.DoJob();

            // Метод GetJobName() является абстрактным в классе AbstractJob
            // Но он уже развернут в определенном подклассе.
            // Поэтому вы можете его вызвать.
            String jobName = job1.GetJobName();

            Console.WriteLine("Job Name 1= " + jobName);


            // Создать объект AbstractJob из Constructor класса CSharpCoding.
            AbstractJob job2 = new CSharpCoding();

            // Вызвать метод DoJob().
            job2.DoJob();

            String jobName2 = job2.GetJobName();

            Console.WriteLine("Job Name 2= " + jobName2);

            // Создать объект AbstractJob из Constructor класса BuildHouse.
            AbstractJob job3 = new BuildHouse();

            job3.DoJob();

            String jobName3 = job3.GetJobName();

            Console.WriteLine("Job Name 3= " + jobName2);


            Console.ReadLine();
        }
    }

}
Результат запуска примера:

4- Обзор про Interface

Мы знаем что один класс может быть расширен только из другого класса.
// Класс B является подклассом A, или можно сказать B расширен (extends) из A.
// CSharp только позволяет расширить класс из единственного класса.
public class B : A  
{
  // ....
}

// В случае вы не указываете из какого класса расширен этот класс.
// CSharp сам понимает, что этот класс расширен из Object.
public class B
{

}

// Способ объявления класса ниже, и способ выше одинаковы.
public class B : Object
{

}
Но класс может расшириться из нескольких интерфейсов (interface).
// Класс может быть расширен только из одного другого класса.
// Но может быть расширен из разных Interface.
public class Cat : Animal, CanEat, CanDrink
{

     // ....
}

Характеристики интерфейса в CSharp.

  1. Интерфейс имеет внутренний (internal) или общий (public) модификаторы (modifier), если не указан,  то по умолчанию будет внутренним
  2. Интерфейс не может определить поля (Field).
  3. Его методы являются абстрактными (abstract)  и общедоступными (public), а тело функции отсутствует. Но когда вы объявляете метод, вам не разрешается специфицировать public или abstract.
  4. Интерфейс не имеет конструктора (constructor).

5- Структура interface (интерфейса)

Interface (Интерфейс) в CSharp может объявлять модификаторы public или  internal, если нет, он будет автоматически рассматриваться как internal. Интерфейс с public модификатором можно использовать везде, для интерфейса с internal модификатором можно использовать только внутри Assembly.

Assembly является компилированным (compile) продуктом вашего кода, обычно это DLL, но EXE так же может считаться assembly. Это самая маленькая единица развертывания любого проекта .NET.

Assembly обычно содержит код .NET по MSIL (Microsoft Intermediate language - Промежуточный язык) который будет компилирован в машинный код (Native code) ("JITted" - компилированный компилятором Just-In-Time) в первый раз будет выполняться на компьютере. Это код который компилирован и будет храниться в Assembly и переиспользован для следующих вызовов.

NoAccessModifierInterface.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // Это Interface не объявляет 'access modifier'.
    // По умолчанию его 'access modifier' будет являться 'internal'.
    // Он только используется внутри одного Assembly.
     interface NoAccessModifierInterface
    {

    }

}
Методы интерфейса абстрактны и публичны, и нет тела. Но когда вы объявляете, вам не разрешается указывать публичные (public)  или абстрактные (abstract).
CanMove.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // Данный Interface определяет вещи со способностью передвигаться.
    public interface CanMove
    {

        // Методы в Interface все являются публичными и абстрактными (public abstract)
        // (Но вам не разрешается писать public или abstract здесь).
        void Run();

        // Назад.
        void Back();

        // Возвращает скорость работы.
        int GetVelocity();

    }

}
CanDrink.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // Данный Interface определяет вещи со способностью пить.
    public interface CanDrink
    { 

          void Drink(); 

    }

}
CanEat.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // Данный Interface определяет вещи со способностью кушать.
    public interface CanEat
    {

          void Eat(); 
    }

}

6- Класс выполняющий интерфейс

Когда класс выполняет интерфейс, вы должны выполнить или объявить все методы, которые включены в Interface.  
  1. Если вы выполняете определенный метод интерфейса, вы должны написать содержание для метода, объявить метод общедоступным.
  2. Если вы не выполняете  определенный метод интерфейса, вы должны объявить его в классе с ключевым словом "public abstract" и не писать содержимое метода.
Давайте посмотрим на пример выполнение интерфейса  CanMove для класса Animal.
Animal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // Animal (Класс, симулирующий класс животных)
    // Расширен из класса Object (Несмотря на то, что не написано ясно).
    // И объявляет выполнение (implements) interface CanMove.
    // Interface CanMove имеет 3 абстрактных метода.
    // Данный класс только выполнил 1 метод CanMove.
    // Поэтому он должен быть объявлен как 'abstract'.
    // Остальные методы должны быть объявлены как  'public abstract'.
    public abstract class Animal : CanMove
    {

        // Выполнить метод Run() interface CanMove.
        // Вам нужно написать содержание метода.
        // Modifier должен быть public.
        public void Run()
        {
            Console.WriteLine("Animal run...");
        }

        // Если данный класс не выполнит определенный метод Interface
        // вам нужно переписать его как абстрактный метод.
        // (Всегда 'public abstract')
        public abstract void Back(); 

        public abstract int GetVelocity();

    }


}
Cat класс унаследует от класса Animal и выполнит 2 интерфейса CanDrink и CanEat.
Cat.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    // Класс Cat расширен из класса Animal и выполняет 2 interface CanEat, CanDrink.
    public class Cat : Animal, CanEat, CanDrink
    {

        private String name;

        public Cat(String name)
        {
            this.name = name;
        }

        public String getName()
        {
            return this.name;
        }

        // Выполняет абстрактный метод у Animal.
        // (Нужно ясно написать 'override' ).
        public override void Back()
        {
            Console.WriteLine(name + " cat back ...");
        }

        // Выполнить абстрактный метод Animal.
        // (Нужно ясно написать 'override' )
        public override int GetVelocity()
        {
            return 110;
        }

        // Выполнить метод у interface CanEat    
        public void Eat()
        {
            Console.WriteLine(name + " cat eat ...");
        }

        // Выполнить метод у  interface CanDrink
        public void Drink()
        {
            Console.WriteLine(name + " cat drink ...");
        }

    }

}
Mouse.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{
    public class Mouse : Animal, CanEat, CanDrink
    {

        // Выполняет абстрактный метод класса Animal.
        // (Нужно иметь ключевое слово 'override').
        public override void Back()
        {
            Console.WriteLine("Mouse back ...");
        }

        // Выполняет абстрактный метод класса Animal.
        public override int GetVelocity()
        {
            return 85;
        }

        // Выполняет метод у interface CanDrink.
        public void Drink()
        {
            Console.WriteLine("Mouse drink ...");
        }

        // Выполняет метод у interface CanEat.
        public void Eat()
        {
            Console.WriteLine("Mouse eat ...");
        }

    }

}
AnimalDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AbstractClassInterface
{

    public class AnimalDemo
    {

        public static void Main(string[] args)
        {

            // Создать объект CanEat.
            // Объект объявлен как CanEat
            // Но на самом деле Cat.
            CanEat canEat1 = new Cat("Tom");

            // Объект объявлен как CanEat
            // Но на самом деле Mouse.
            CanEat canEat2 = new Mouse();

            // Полиморфизм (Polymorphism) ясно показывается здесь.
            // CSharp всегда знает реальный вид объекта.
            // ==> Tom cat eat ...
            canEat1.Eat();

            // ==> Mouse eat ...
            canEat2.Eat();

            bool isCat = canEat1 is Cat;// true

            Console.WriteLine("catEat1 is Cat? " + isCat);

            // Проверить является ли 'canEat2' мышью или нет?.
            if (canEat2 is Mouse)
            {
                // Cast
                Mouse mouse = (Mouse)canEat2;

                // Вызвать метод Drink() (Унаследован от CanDrink).
                mouse.Drink();
            }

            Console.ReadLine();
        }
    }

}
Запуск примера: