Как предотвратить наследование абстрактного класса с публичными производными классами в других сборках? - PullRequest
6 голосов
/ 22 февраля 2012

Я хочу написать что-то вроде следующего:

    internal class InternalData
    {
    }

    public class PublicData
    {
    }

    abstract internal class Base {
        internal Base() { }

        private static InternalData CreateInternalDataFromPublicData(PublicData publicData)
        {
            throw new NotImplementedException();
        }

        abstract protected void DoProcess(InternalData internalData);

        public void Process(PublicData publicData)
        {
            InternalData internalData = CreateInternalDataFromPublicData(publicData);
            DoProcess(internalData);
        }
    }

    public sealed class Derived : Base
    {
        protected override void DoProcess(InternalData internalData)
        {
            throw new NotImplementedException();
        }
    }

То есть Base содержит некоторую внутреннюю логику и не предназначен для наследования классами вне моей сборки;и Derived доступен снаружи.InternalData также содержит некоторую внутреннюю логику и, поскольку она никогда не будет использоваться извне, я также хочу сделать ее внутренней.

Конечно, приведенный выше код не будет компилироваться как Base не должен быть менее доступным, чем Derived.Я могу установить Base равным public, это нормально, но это приводит к другой проблеме.Если Base является общедоступным, то, возможно, в некоторой другой сборке может быть ExternalDerived : Base.Но Base.DoProcess принимает InternalData в качестве аргумента, так что ExternalDerived не может его реализовать (поскольку он не знает о InternalData).Внутренний беспараметрический конструктор Base предотвращает создание любых ExternalDerived экземпляров, и, таким образом, никто не будет реализовывать ExternalDerived.DoProcess, и не требуется InternalData публичное раскрытие, но компилятор этого не знает.

Как можноя переписал код выше, чтобы был абстрактный метод DoProcess(InternalData) и чтобы класс InternalData был внутренним?

Ответы [ 5 ]

4 голосов
/ 22 февраля 2012

Чтобы сделать InternalData внутренним, DoProcess должно быть private или internal (или InternalAndProtected, но C # не поддерживает эту функцию CLR). Это не может быть protected или protected internal.

internal abstract DoProcess(InternalData internalData);

Возможно, я бы также добавил internal abstract void DoNotInheritFromThisClassInAnOutsideAssembly() участника. Это не позволяет никому вне сборки наследовать от вашего класса, потому что они не могут реализовать этот член и получают разумную ошибку компилятора. Но вы не можете сделать класс Base внутренним.


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

1 голос
/ 22 февраля 2012

Базовый тип должен быть доступен, потому что в противном случае становится невозможным выяснить его базу. Ваш Base напрямую связан с System.Object, но как пользователь Derived узнает об этом? Откуда он знает, что Base не является производным от другого открытого типа, и члены этого типа должны быть доступны?

Если вы отметите все во Base внутреннем, кроме самого класса, вы уже помешали другим сборкам делать с ним что-нибудь полезное. Другими словами, если вы сделаете DoProcess внутренним, вы сможете предотвратить превращение InternalData в public.

Да, по общему признанию, это допускает ошибки в вашей собственной сборке, если другие классы пытаются вызвать DoProcess. К сожалению, нет модификатора доступа «доступно из производных классов в той же сборке», только «доступно из производных классов», «доступно из той же сборки» и «доступно из производных классов и доступно из той же сборки». (На самом деле .NET поддерживает это, но C # не поддерживает.)

1 голос
/ 22 февраля 2012

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

0 голосов
/ 22 марта 2013

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

Ваш пример сведен к минимуму:

abstract internal class Base {
    internal protected abstract void DoProcess();

    public void Process() {
        DoProcess();
    }
}

public sealed class Derived : Base {
    internal protected override void DoProcess() {
        throw new NotImplementedException();
    }
}
0 голосов
/ 22 февраля 2012

Установите Base равным public.

public abstract class Base {...

Изменить базу. Процесс:

protected virtual void DoProcess<T>(T internalData)
{
    if (!(internalData is InternalData))
    {
        throw new ArgumentOutOfRangeException("internalData");
    }
}

Изменить Derived.DoProcess:

protected override void DoProcess<T>(T internalData)
{
    base.DoProcess(internalData);
    // Other operations
}
...