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

Posted

技术标签:

【中文标题】如何防止具有公共派生类的抽象类在其他程序集中被继承?【英文标题】:How to prevent an abstract class with public derived classes from being inherited in other assemblies? 【发布时间】:2012-03-12 16:43:05 【问题描述】:

我想写如下内容:

    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 类将是内部的?

【问题讨论】:

【参考方案1】:

要使 InternalData 内部化,DoProcess 必须是 privateinternal (或InternalAndProtected,但 C# 不支持此 CLR 功能)。不能是protectedprotected internal

internal abstract DoProcess(InternalData internalData);

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


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

【讨论】:

。我尝试 +2,但没有成功。 internal Base() 具有与 internal abstract void DoNotInheritFromThisClass() 相同的 IIUC 效果(即阻止其他程序集中的任何人创建可实例化的派生类)。我希望首先保护DoProcess,因为它纯粹是一个实现细节。 @penartur 只需将DoProcess 设为内部而不是受保护。然后您可以将InternalData 设为内部。 @CodeInChaos 抱歉没有说清楚。无论如何,我不希望任何人从我的Base 扩展(否则他们自欺欺人)。主要问题是我不希望任何人看到我的InternalData,除非他们使用反射。【参考方案2】:

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

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

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

所以,像这样的类:

public abstract BaseClass

    private protected BaseClass() 

在程序集外部有效地密封,而在程序集中仍然可以继承。

【讨论】:

【参考方案3】:

闻起来你应该使用composition instead of inheritance。抱歉,这是一个非常模糊的答案。我现在正在考虑更多..

【讨论】:

您的链接无效。组合对我发布的确切代码示例有意义,但在我的实际代码继承中更有意义。基本上,我有一堂有很多私有方法和字段的课程;然后我需要扩展它 - 使一些私有方法受到保护,这导致了以下问题。在这里使用组合似乎会导致编写大量代码,毕竟,在我的真实案例中,Derived 实际上Base 的一个特例。 修复了链接,反正它只是一个谷歌搜索。如果没有看到所有代码,很难更具体。是的,组合通常会导致编写更多代码,但这不一定是坏事。 数据类是没有方法的不可变 POCO 吗? 假设它是例如AbstractCarTruck : AbstractCar,还有 public Run()abstract protected RotateableWheel[] GetWheels(),我当然不希望其他程序集中的任何人知道 RotateableWheel 类(除非他们当然使用反射)。 是的,InternalData 是不可变的,虽然我看不出它有什么不同。【参考方案4】:

基类型必须是可访问的,否则就不可能找出它的基。你的Base 直接来自System.Object,但是Derived 的用户怎么知道呢?它怎么知道Base 不是从另一个公共类型派生的,并且应该使该类型的成员可用?

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

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

【讨论】:

"基类型必须是可访问的,否则就无法找出它的基数" - 我明白了,将Base 公开就可以了为了我。您对 C# 中缺少适当的访问修饰符的解释确实有道理,到目前为止,这是最好的答案。尽管如此,通过internal Base() 可以看出没有人会在其他程序集中实现DoProcess,并且它的参数不需要公开,所以也许有一些解决方法。 正确,但要弄清楚这一点要困难得多,因为它还依赖于您自己的程序集中从Base派生的所有类都是sealed这一事实。【参考方案5】:

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

【讨论】:

哇,这肯定是我从未想过的肮脏方式!我接受了你的回答,因为它似乎是唯一真正解决问题的答案,不过我最好还是接受 InternalData 的公开 :) @penartur 您仍然没有解释是什么阻止您简单地将DoProcess 设为内部。 internal DoProcess 虽然不是最佳选择,但肯定比这个 hack 更干净。 正如我所说,我不会在我的代码中实现这个 hack,而是公开InternalData。至于你的问题,基本上是internal InternalData; internal DoProcesspublic InternalData; protected DoProcess之间的选择;虽然如果有这样的选择我更喜欢internal InternalData; protected DoProcess,但在这两个中我更喜欢后者。【参考方案6】:

其实很简单。您只需要派生类实现一个抽象的内部方法。库外的类将无法实现抽象方法,因此在编译时失败。

您的示例,简化为基本要素:

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

【讨论】:

你试过编译你的代码吗?出于显而易见的原因,派生类不允许比基类更易于访问。无法从内部类派生公共类。 @penartur:这个例子不是很好,但是只要所有的类都是抽象的或密封的,这个技巧是可行的。

以上是关于如何防止具有公共派生类的抽象类在其他程序集中被继承?的主要内容,如果未能解决你的问题,请参考以下文章

继承与派生

Python基础----继承派生组合接口和抽象类

python基础之继承派生组合接口和抽象类

201803280002

Python开发基础-Day18继承派生组合接口和抽象类

降低基类属性的可见性