C# унаследовать класс сразу от нескольких классов, не изменяю родительские классы

у меня есть класс, к примеру: MyClass. И при работе в нём, я хочу пользоваться методами других классов(системных), но я не хочу в каждом методе заново определять объекты этих классов, я просто хочу обращаться к элементам системных классов, с помощью: this.SystemClass. Всё прекрасно, когда я наследую только один родительский класс. Но я хочу добавить их больше, естественно c# не поддерживает множественное наследование. Я пришёл к использованию интерфейсов, конкретно к этой схеме:

class SystemClass1
{
  m1();
}

interface ISystemClass2
{
  m2();
}

class SystemClass2 : ISystemClass2
{
  m2();
}

class C : SystemClass1, ISystemClass2
{
  SystemClass2 BObject;
  m1();
  m2() { BObject.m2(); }
}

Но здесь нам надо прописать наследование в родительском(в моём случае системном классе). Вопрос, как мне унаследоваться от двух системных классов?

P.S в C# нуб(знаю его 2 дня, вернее не знаю его :D ) поэтому пожалуйста понятнее


Ответы (2 шт):

Автор решения: aepot

Наследование в первую очередь нужно для реализации общих публичных членов классов, когда между классами есть общее - родитель. Это позволяет избежать дублирования кода.

Наследование полезно тогда, когда хочется создать коллекцию из разных классов, при этом пользоваться их общими методами от родителя не приводя к конкретному типу. Здесь на помщь приходит еще полиморфизм.

Наследование идет от абстрактного к конкретному.

Когда вы наследуете конкретный тип от абстракции, он перенимает все реализации родителя на себя. Если вам вдруг для какой-то задачи потребовалось 2 родителя, например:

class A1 { }
class A2 { }

class B : A1, A2 { } // ошибка компиляции

Тогда вы столкнетесь с ограничением языка C#, потому что наследоваться можно только от одного родителя.

Если вам нужны члены обоих базовых классов в наследнике, при необходимости, это ограничение можно обойти унаследовав базовые классы один от второго:

class A1 { }
class A2 : A1 { }

class B : A2 { }

Но этот обход не всегда подходит, в зависимости от конкретной задачи. Или как в вашем случае, когда технически невозможно изменить реализацию родителей так, чтобы они наследовались друг от друга.

Наследование используется для сборки целостного объекта, который полноценно умеет что-то делать. Но при такой сборке класса обычно учитывается Принцип единой ответственности (SRP из SOLID), это когда класс умеет что-то делать одно и в рамках единой задачи.

То есть если это к примеру класс "Бомба", то он умеет только тикать и взрываться, он не умеет себя запускать или останавливать, переносить из одного угла комнаты в другой, мяукать или стирать шнурки. Только тикать и взрываться.

То есть, другими словами, просто желание инкапсулировать в один класс всё подряд - не повод для наследования от всего что нужно. Наследование - не для этого используется. Если вам нужна мяукающая перед взрывом бомба, создайте класс "Мяукало", передайте его в конструктор "Бомбы" и пусть она использует это мяукало перед взрывом. Такой подход облегчит доработку приложения например, вы однажды передумали и решили что она будет мяукать не в консоль, а в наушники. И ура, если вы всё верно предусмотрели, то изменения в класс "Бомбы" вообще не придется вносить. Это и есть та самая польза от принципа единой отвественности.


Кстати, передача экземпляра класса в конструктор другого класса - по-модному называется "внедрение зависимости" (DI = Dependency Injection), а если быть конкретнее, то "внедрение в конструктор" (Constructor Injection). И если вы однажды...точнее не если, а...когда вы однажды заинтересуртесь, как же из этой кучи классов собрать единое целое и ни где не накосячить, при этом максимально быстро и просто в эту кучу добавлять новые компоненты, вы рано или поздно наткнетесь на класс, который умеет втыкать зависимости в конструкторы (и не только в них) автоматически. Когда какой-то класс сам решает, где взять экземпляр, требуемый для текущего класса в конструкторе и создает этот экземпляр для вас автоматически - называется что он взял управление экземлярами на себя. То есть не текущий класс создает экземпляры с помощью new, а какой-то другой, это называется "инверсия управления" (IoC = Inversion of Control), и такой управляющий класс называется "контейнер", об этом выше в комментарии и написал @EvgeniyZ. Не торопитесь в эту тему нырять, она не для начинающих, просто пока вам достаточно знать, что оно вот такое есть.

Ах, да, есть еще же интерфейсы. Так вот, они к наследованию имеют очень косвенное отношение. Их задача определить шаблон публичной реализации для текущего класса, вы не сможете определить в интерфейсе приватные члены. Реализовать один и тот же интерфейс могут совершенно не связанные родственными связями между собой классы. Один класс может реализовывать (не наследовать, а именно реализовывать) несколько интерфейсов одновременно.

→ Ссылка
Автор решения: Blackmeser

Если нужны только статические методы классов, то можно их подключить локально:

using static System.Math;
using static System.Threading.Thread;

И потом в коде их можно будет использовать сокращённо:

int max = Max(1,2);
//вместо System.Math.Max(1,2);

int id = CurrentThread.ManagedThreadId;
//вместо System.Threading.Thread.CurrentThread.ManagedThreadId;

Можно пойти чуть дальше и натянуть сову на глобус с костылями:

class MyStaticMethods
{
    public static string GiveMeHello()
    {
        return "Hello";
    }
}

using static MyStaticMethods;
string hello = GiveMeHello(); //вместо MyStaticMethods.GiveMeHello();

Можно пойти чуть дальше и смешать всё это с кислотой, сделав динамическую реализацию:

interface IMyConfigure
{ 
    public int Port { get; set; }
    public string Name { get; set; }
    public object UniversalContainer { get; set; }
}
class MyConfigure
{
    public static IMyConfigure MyConfig { get; set; }
    public static bool IsMyConfigExist => MyConfig != null;
    public static int GiveMePort()
    {
        return MyConfig.Port;
    }
    public static string GiveMeName()
    {
        return MyConfig.Name;
    }
    public static object GiveMeUniversalContainer()
    {
        return MyConfig.UniversalContainer;
    }
}
class ShelockMyConfigure : IMyConfigure
{
    public int Port { get; set; } = 1;
    public string Name { get; set; } = "Bubenchik";
    public object UniversalContainer { get; set; } = DateTime.Now;
}

И получить уже не совсем статическое поведение:

using static MyConfigure;

MyConfig = new ShelockMyConfigure() { Port = 2 };
GiveMePort() //2
GiveMeName() //"Bubenchik"
GiveMeUniversalContainer() //момент первого обращения

Не полноценно то, что нужно, но где-то да поможет, всё локально загнать не получится.

→ Ссылка