簡體   English   中英

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

[英]How to prevent an abstract class with public derived classes from being inherited in other assemblies?

我想寫如下內容:

    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類將是內部的?

要使InternalData內部數據, DoProcess必須是privateinternal (或InternalAndProtected ,但 C# 不支持此 CLR 功能)。 它不能被protectedprotected internal

internal abstract DoProcess(InternalData internalData);

我可能還會添加一個internal abstract void DoNotInheritFromThisClassInAnOutsideAssembly()成員。 這可以防止程序集之外的任何人從您的類繼承,因為他們無法實現該成員,並且會收到合理的編譯器錯誤。 但是您不能將Base類本身設置為內部。


我會考慮重構代碼,這樣你就沒有通用的基類了。 可能通過使用一些internal接口和組合。

由於 C# 7.2 有private protected訪問修飾符,這意味着“僅可用於同一程序集中的派生類”。

換句話說,它必須同時滿足internal AND protected條件,這與適用於internal OR protected protected internal不同。

您可以將基類的構造函數標記為private protected ,從而有效地防止在程序集外部通過此構造函數繼承該類,同時仍然允許在該程序集(以及程序集的朋友)內進行繼承。

所以,一個這樣的類:

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

在程序集之外被有效地密封,同時在程序集中仍然可以繼承。

聽起來你應該使用組合而不是繼承 抱歉,這是一個非常模糊的答案。 我現在想的更多。。

基類型必須是可訪問的,否則就不可能找出它的基類型。 您的Base直接從System.Object派生,但Derived的用戶如何知道這一點? 它怎么知道Base不是從另一個公共類型派生的,並且該類型的成員應該可用?

如果您在Base內部標記所有內容,除了類本身,您已經阻止了其他程序集對它做任何有用的事情。 換句話說,如果將DoProcess內部,則可以防止InternalData變為public

是的,誠然,如果其他類嘗試調用DoProcess ,這會導致您自己的程序DoProcess 不幸的是,沒有“可從同一程序集中的派生類訪問”訪問修飾符,只有“可從派生類訪問”、“可從同一程序集訪問”和“可從派生類訪問並從同一程序集訪問”。 (實際上,.NET 確實支持它,但 C# 不支持。)

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
}

它實際上很簡單。 您只需要派生類實現抽象內部方法。 庫外的類將無法實現抽象方法,因此在編譯時會失敗。

您的示例已最小化為基本要素:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM