如何防止具有公共派生类的抽象类在其他程序集中继承?

时间:2012-02-22 10:50:31

标签: c# inheritance public access-modifiers internal

我想写下面的内容:

    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包含一些内部逻辑,并不打算由我的程序集之外的类继承;并且可以从外部访问DerivedInternalData还包含一些内部逻辑,因为它(并且应该)永远不会从外部使用,我也希望将其作为内部逻辑。

当然上面的代码不会编译,因为Base不应该比Derived更难访问。我可以将Base设置为public,这很好,但它会导致另一个问题。 如果Base是公开的,那么其他程序集中可能会有一些ExternalDerived : Base。但是Base.DoProcess接受InternalData作为其参数,因此ExternalDerived无法实现它(因为它不知道InternalData)。 内部无参数Base构造函数阻止创建任何ExternalDerived实例,因此没有人会实现ExternalDerived.DoProcess并且不需要InternalData公开曝光,但编译器不知道它。

我如何重写上面的代码,以便有一个抽象的DoProcess(InternalData)方法,以便InternalData类在内部?

6 个答案:

答案 0 :(得分:4)

要使InternalData为内部DoProcessprivate必须为internalInternalAndProtected  (或protected,但C#不支持此CLR功能)。它不能是protected internalinternal abstract DoProcess(InternalData internalData);

internal abstract void DoNotInheritFromThisClassInAnOutsideAssembly()

我可能还会添加Base成员。这可以防止程序集之外的任何人从您的类继承,因为他们无法实现该成员并且他们得到合理的编译器错误。但是你不能让internal类本身在内部。


我会考虑重构代码,这样你就没有共同的基类。可能通过使用一些{{1}}接口和组合。

答案 1 :(得分:1)

闻起来应该使用composition instead of inheritance。对不起,这是一个非常含糊的答案。我现在正在考虑这个问题..

答案 2 :(得分:1)

基本类型必须是可访问的,否则,无法找出基础。您的Base直接来自System.Object,但Derived的用户如何知道?它是如何知道Base不是从其他公共类型派生出来的,那个类型的成员应该可用?

如果你在Base内部标记了所有内容,除了类本身,你已经阻止了其他程序集对它做任何有用的事情。换句话说,如果您将DoProcess设为内部,则可以阻止InternalData成为public

是的,诚然,如果其他类试图调用DoProcess,这会在您自己的程序集中出现错误。遗憾的是,没有“可从同一程序集中的派生类访问”访问修饰符,只能“从派生类访问”,“可从同一程序集访问”和“可从派生类访问并可从同一程序集访问”。 (实际上,.NET确实支持它,但C#不支持它。)

答案 3 :(得分:0)

Base设为public

public abstract class Base {...

更改Base.DoProcess:

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
}

答案 4 :(得分:0)

自C#7.2起,存在private protected访问修饰符,这意味着“仅适用于同一程序集中的派生类”。

换句话说,它必须同时满足internalprotected的条件,与protected internal适用于internalprotected的情况不同。

您可以将基类的构造函数标记为private protected,从而有效地防止在程序集外部通过该构造函数继承该类,同时仍然允许该程序集(以及程序集的朋友)内的继承。

因此,这样的类:

public abstract BaseClass
{
    private protected BaseClass() {}
}

在程序集外部有效密封,同时仍可在程序集内部继承。

答案 5 :(得分:-2)

实际上很直接。您只需要派生类实现抽象内部方法。库外的类将无法实现抽象方法,因此在编译时失败。

您的示例,最小化到基本要素:

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();
    }
}