Позволяет классу проходить сам по себе как параметр для общего базового класса зла?

Я впервые увидел, что коллега это сделал, когда он реализовал пулы объектов. Он прошел класс, который собирался объединяться как параметр в общий базовый класс. Этот базовый класс заложил код объединения.

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

Одним из недостатков этой конструкции является то, что она "сжигает базовый класс". Вы не можете ввести общий базовый класс в середине иерархии. Эта проблема может быть вне темы.

Ниже приведен мыслящий пример:

public abstract class Singleton<T> where T : class
{
    public static T Instance { get; private set; }

    public Singleton()
    {
        if (Instance != null)
            throw new Exception("Singleton instance already created.");
        Instance = (T) (object) this;
    }
}

public class MyClass : Singleton<MyClass>
{
}

Улучшенный код:

public abstract class Singleton<T> where T : Singleton<T>
{
    public static T Instance { get; private set; }

    public Singleton()
    {
        if (Instance != null)
            throw new Exception("Singleton instance already created.");
        Instance = (T) this;
    }
}

public class MyClass : Singleton<MyClass>
{
}

Ответы

Ответ 1

Нет; это хорошо известный шаблон, называемый CRTP.
Это особенно полезно в С++ в качестве альтернативы виртуальным методам.

Вы можете увидеть его внутри .Net framework в IComparable<T> и IEquatable<T>.

Для повышения надежности добавьте where T : Singleton<T>

Ответ 2

SLaks корректен - это полезный шаблон, как правило, когда вы хотите предоставить код в базовом классе, который строго типизирован для вашего производного класса.

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

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

public abstract class Beverage<T> where T : Beverage<T>
{
    public abstract T Blend(T drink1, T drink2);
}

public class Tea : Beverage<Tea>
{
    public override Tea Blend(Tea drink1, Tea drink2)
    { 
        // Blend tea here.
    }
}
public class Coffee : Beverage<Coffee>
{
    public override Coffee Blend(Coffee drink1, Coffee drink2)
    { 
        // Blend coffee here.  Although coffee is nasty, so
        // why you'd want to is beyond me.
    }
}

Когда вы читаете о CRTP, стоит помнить, что шаблоны С++ только внешне похожи на С# generics. Основное различие заключается в том, что шаблоны являются эффективным инструментом генерации кода, который работает во время компиляции, тогда как обобщенные типы С# поддерживаются во время выполнения.

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