Я не понимаю наследование с базовыми классами

Мое понимание наследования довольно простое.

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

Это позволило бы мне поместить каждый объект в один список, чтобы я мог избавиться от всего за один раз, когда я хочу очистить. Дело в том, что класс в C# может наследовать только один раз, а в остальных случаях использовать только интерфейсы.

Теперь, как такие классы, как Thread, могут наследовать от моего базового класса без необходимости заново кодировать класс Thread?

Это может показаться глупым, но вкратце: я хочу позволить уже существующим классам наследовать базовый класс по моему выбору без потери его функциональности. (Чтобы все классы, используемые в проекте, имели общую базу, чтобы их можно было суммировать в один тип объекта, например List)

Это вообще возможно?


person Rakku    schedule 28.01.2021    source источник
comment
Дело в том, что класс в C# может наследовать только один раз, а в остальных случаях использовать только интерфейсы — кто вам это сказал? Практических ограничений на глубину дерева наследования нет.   -  person Caius Jard    schedule 28.01.2021
comment
Я хочу позволить уже существующим классам наследовать базовый класс по моему выбору — вы написали эти уже существующие классы? как такие классы, как Thread, могут наследовать от моего базового класса без необходимости написания основного кода класса Thread - они не могут   -  person Caius Jard    schedule 28.01.2021
comment
Каждый класс может наследоваться только от одного базового класса, но следующий класс может наследовать этот класс и следующий класс. Там нет реального предела.   -  person SimonC    schedule 28.01.2021
comment
Возможно ли это вообще? - все в c# уже наследуется от Object, но вы не можете написать код, который изменяет то, от чего наследует другой класс, не имея исходного кода для другого класса   -  person Caius Jard    schedule 28.01.2021
comment
@CaiusJard Нет, он имеет в виду объекты из .Net framework.   -  person SimonC    schedule 28.01.2021
comment
Извините за путаницу, @SimonC прав, это классы .Net framework. В основном я хочу изменить базовый класс на общий для всех классов, которые я хочу использовать в проекте.   -  person Rakku    schedule 28.01.2021
comment
Вы неправильно поняли концепцию Дело в том, что класс в C# может наследовать только один раз и в противном случае использовать только интерфейсы, что означает, что класс может наследовать только от одного класса напрямую одновременно .   -  person Miraziz    schedule 28.01.2021
comment
@CaiusJard дело в том, что, к сожалению, объект базового класса не реализует интерфейс IDisposable, поэтому я не могу использовать List‹object›, а затем очищать объекты в списке с помощью .Dispose().   -  person Rakku    schedule 28.01.2021
comment
@Jamiec подожди что? Я что-то пропустил тогда? Насколько я знаю, список может принимать только один тип объекта за раз, или я ошибаюсь? Даже если нет, мне придется использовать разные foreach для каждого типа объекта, чтобы использовать .Dispose()   -  person Rakku    schedule 28.01.2021
comment
@Rakku у вас может быть список, в котором тип является интерфейсом. Смотрите мой ответ.   -  person Jamiec    schedule 28.01.2021
comment
@Jamiec Ваша ссылка, к сожалению, у меня не работает.   -  person Rakku    schedule 28.01.2021
comment
@Rakku это было для XYProblem - я все еще думаю, что вы спрашиваете о том, как вы думаете что-то должна быть решена, а не реальная проблема, с которой вы столкнулись. Почитайте.   -  person Jamiec    schedule 28.01.2021


Ответы (3)


Пример, который вы привели, включает интерфейс IDisposable. В C# класс может реализовывать несколько интерфейсов.

С другой стороны, в C# класс может наследоваться только от одного класса.

Сказав это, не всегда хорошая идея наследовать от классов, так как это очень ограничивает. Вместо этого я бы предложил состав: wiki

Например, я бы предложил что-то вроде шаблона проектирования композиции (это небольшая вариация, так как композит не имеет набора объектов, а только один объект):

public interface IFoo
{
    void DoFooStuff();
}

public class Foo : IFoo
{
    public void DoFooStuff() {...};
}

public class ShouldImplementFoo : IFoo
{
    // Composition, you need to initialize it some time
    private Foo _foo;
    
    public void DoFooStuff() { _foo.DoFooStuff(); }
}

ИЗМЕНИТЬ

После переосмысления вашей проблемы вы, кажется, хотите поместить некоторый набор объектов (разных классов) в список, чтобы вы могли выполнить некоторое определенное действие для каждого объекта в списке.

Самое простое: определить интерфейс с вашим определенным действием:

public interface IMyIntf
{
    void MyDesiredAction();
}

А теперь сделайте так, чтобы все классы реализовали это. Теперь вы можете подумать Ну, теперь мне нужно внедрить этот метод везде... - ничего подобного! Просто используйте шаблон проектирования адаптера, например, имея класс:

public class MyConcreteClass
{
    public void ActionIWishToExecuteOnThisObject() 
    { ... }
}

вы просто измените его на:

public class MyConcreteClass : IMyIntf
{
    public void ActionIWishToExecuteOnThisObject() 
    { ... }

    // implement interface
    public void MyDesiredAction()
    {
        ActionIWishToExecuteOnThisObject();
    }
}

Теперь вы можете использовать какой-нибудь агрегат, например List<IMyIntf>, поместить туда все интересующие вас объекты и сделать:

foreach(var item in myList)
    item.MyDesiredAction();
person Michał Turczyn    schedule 28.01.2021

Если ваша цель состоит в том, чтобы просто сохранить список объектов, которые можно удалить, вам нужен файл List<IDisposable>. В нем может храниться любой класс, который прямо или косвенно реализует этот интерфейс.

var disposables = new List<IDisposable>();
disposables.Add(new MyClass1());
disposables.Add(new SomeClass());
disposables.Add(new AnotherClass());

disposables.ForEach(d => d.Dispose());

Является ли это достойным шаблоном, я не уверен!

person Jamiec    schedule 28.01.2021
comment
Вау, я не знал, что могу просто использовать интерфейс в качестве типа списка! Это отличный ярлык. Какой шаблон вы бы использовали для очистки? - person Rakku; 28.01.2021
comment
Не этот! Дело в том, что у вас редко бывает совершенно разрозненный список случайных классов, которые нужно очистить за один раз. Какой бы класс ни создавал экземпляр объекта, он должен нести ответственность за последующую очистку. - person Jamiec; 28.01.2021
comment
А, теперь я вижу, где недоразумение. Я не хотел использовать этот шаблон для очистки полных классов. Этот шаблон используется в каждом классе, где класс очищает свои компоненты, которые я создал. Список содержит компоненты, а не целые классы. У меня была идея, что мне нужно создать более одного списка для компонентов, которые не имеют общего родителя (в котором реализован IDisposable). Но так как теперь я могу просто поместить все в один список, который просто использует интерфейс, та проблема, которая у меня была, исчезла. - person Rakku; 28.01.2021
comment
@Rakku Если вы нашли ответ на свою проблему, отметьте один ответ как принятый. - person Michał Turczyn; 29.01.2021

Класс может наследовать от класса-предка (иначе Object, корень всего).

Также он может реализовывать некоторые интерфейсы (или ни один).

Класс может иметь нереализованные члены как абстрактные, поэтому класс является абстрактным.

Также члены могут быть виртуальными, чтобы включить полиморфизм (абстрактные члены).

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

Нет необходимости повторяться, и в этом одна из сильных сторон ООП.

Делая это, используя абстракцию, инкапсуляцию, наследование и полиморфизм, после изучения реального мира и области, в которой мы работаем, мы можем разработать проекцию этого реального мира, представление в нашем уме, с понятиями, имеющими данные (поля и свойства) и операции (методы), классифицируемые по артефактам (объектам) и такие вещи, как отношения, композиция, агрегация, интерфейсы, компоненты, элементы управления и т. д.

Таким образом, мы можем факторизовать обработки, чтобы они не повторялись, например:

  • Имея класс Animal и дочерние классы Cat и Dog,
  • Мы вкладываем в животное аспекты Size и Color,
  • А также методы Eat и Walk, реализованные для всех животных,
  • И абстрактный нереализованный DoSound.

Следовательно:

  • В конкретных классах Cat и Dog мы не реализуем снова вещи,
  • За исключением DoSound, который индивидуален для каждого реального пола животного.

Что касается интерфейсов, то это виртуальные контракты.

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

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

Пример кода:

public interface IPhotoTaking
{
  Image TakePhoto();
}
public abstract class Animal : IPhotoTaking
{
  public int Size { get; set; }
  public Color Color { get; set; }
  public void Eat() { ... }
  public void Walk() { ... }
  public abstract void DoSOund();
  public Image TakePhoto();
}

public class Cat : Animal // no need to repeat IPhotoTaking
{
  public override void DoSound() { ... }
}

public class Dog : Animal
{
  public override void DoSound() { ... }
}
public abstract class Building :IPhotoTaking
{
  public Image TakePhoto();
}

public class House : Building
{
}

public class Museum : Building
{
}

Теперь, имея этот список, мы можем использовать полиморфизм следующим образом:

var animals = new List<Animal>();

foreach ( var animal in animals )
  animal.DoSound();

Также без полиморфизма и поскольку C# не поддерживает настоящий полиморфизм с оператором алмаза, который все же применяется здесь к List<>, мы можем использовать такой интерфейс:

var items = new List<IPhotoTaking>();

foreach ( var item in items )
  item.TakePhoto();

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

Основные принципы ООП

Что такое абстракция в C#?

Как выбрать между общедоступный, частный и защищенный модификатор доступа?

Ассоциация, композиция и агрегация

Что такое полиморфизм?

В чем разница между интерфейсом и классом?

Об отсутствии истинного универсального полиморфизма и отсутствующий оператор алмаза в C#

person Olivier Rogier    schedule 28.01.2021
comment
Спасибо, объяснение было очень полезным, к сожалению, я могу отметить только один ответ, но ваш тоже был очень хорош :)! - person Rakku; 29.01.2021
comment
@Rakku Просто выберите тот, который лучше всего объясняет вам вещи и помогает вам достичь своей цели так, как вы чувствуете себя лучше всего, чтобы иметь простой, чистый, короткий, понятный, ремонтопригодный, элегантный, надежный, безопасный и очень эффективный без спагетти. - person Olivier Rogier; 29.01.2021