C # - разрешить наследование, но запретить прямое использование конструктора - PullRequest
1 голос
/ 01 апреля 2019

Я хочу разрешить наследование, но запретить прямое построение любого из унаследованных классов.Вместо этого я хочу принудительно использовать пользовательский метод New().

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

В этом случае, невозможно сделать конструктор private или internal.В противном случае вы больше не сможете наследовать от класса вне сборки.

Есть ли какой-нибудь элегантный способ решить эту проблему?Мое текущее решение:

public abstract class Class<This> : MarshalByRefObject where This : Class<This>
{
    private static bool ShouldThrowOnConstruction = true;
    private static readonly object Lock = new object();

    public static This New()
    {
        lock (Lock)
        {
            ShouldThrowOnConstruction = false;
            var instance = (This)new ClassProxy<This>().GetTransparentProxy();
            ShouldThrowOnConstruction = true;
        }
        return instance;
    }

    protected Class()
    {
        if (ShouldThrowOnConstruction)
        {
            throw new InvalidOperationException("Direct use of the constructor is forbidden. Use New() instead.");
        }
    }
}

1 Ответ

4 голосов
/ 01 апреля 2019

Почему бы не использовать статическую фабричную функцию вместо конструктора?

например,

public abstract class Class<This> : MarshalByRefObject where This : Class<This>
{
    public static Class<This> Build()
    {
        var instance = (This)new ClassProxy<This>().GetTransparentProxy();
    }

    protected Class() 
    {        
    }
}

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

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

Редактировать:

Если вы хотите гарантировать, что он не может быть вызван, заставьте конструктор выдать исключение throw new InvalidOperationException("Direct use of the constructor is forbidden. Use Build() instead.");, и ваш метод GetTransparentProxyне должен вызывать new для создания объекта, а вместо этого использовать FormatterServices.GetUninitializedObject() для обхода конструктора.Это должно позволить создать экземпляр, но он немного пахнет кодом.

Примерно так:

public abstract class Class<This> : MarshalByRefObject where This : Class<This>
{
    public static Class<This> Build()
    {
        // Ensure GetTransparentProxy() calls FormatterServices.GetUninitializedObject() to create the object, rather than calling `new`
        var instance = (This)new ClassProxy<This>().GetTransparentProxy();
    }

    protected Class() 
    {
        throw new InvalidOperationException("Direct use of the constructor is forbidden. Use Build() instead.");
    }
}
...