web-dev-qa-db-ja.com

内部抽象クラス:アセンブリの外で使用法を隠す方法は?

抽象基本クラスを持つ共通のアセンブリ/プロジェクトがあり、他のアセンブリに公開したいいくつかの派生クラスがあります。

抽象基本クラスをIntellisenseの他のアセンブリに表示したくないので、internalにすると思いましたが、次のエラーが発生します。

一貫性のないアクセシビリティ:基本クラス 'Settings'はクラス 'IrcSettings'よりもアクセスしにくい....

本当にわかりません。抽象Settingsクラスpublicを作成することを余儀なくされたため、このアセンブリの外に表示されます。

代わりにこのクラスをinternalにするにはどうすればよいですか?

47
m3ntat

私が理解しているように、あなたはあなたの抽象クラスが同じアセンブリ内の他のクラスによってのみ実装されることを望んでいます(例えばそれは内部です)が、派生クラスはパブリックである可能性があります。

これを行う方法は、抽象基本クラスをパブリックにすることですが、内部のデフォルトコンストラクターを指定します。

public abstract class MyClass
{
    internal MyClass() { }
}

これにより、MyClass(およびそのメンバー)がアセンブリ外のクラスから見えるようになり、使用できるようになりますが、アセンブリ外のクラスはそれから継承できません(コンパイルエラーが発生します)。

編集:canが外部アセンブリによって表示されるクラスがMyClassから継承する場合、MyClassがseenになることを防ぐことはできません-たとえば、Intellisenseに表示されます。ただし、上記の手順に従うことで、それらがsedになるのを防ぐことができます。

87
Rex M

クラスの継承階層全体を表示する必要があるため、抽象基本クラスはパブリックである必要があります。これにより、ポリモーフィズムが機能し、有効になります。ただし、すべての基本クラスのメンバーは内部(コンストラクターを含む)であるため、アセンブリの外部では使用できません

9
thecoop

実際に達成しようとしていることにはあまりメリットはありませんが、実際に達成しようとしていることはこれに似ています。

すべてが内部にある1つのアセンブリに抽象基本クラスを作成します。そのアセンブリのAssemblyInfoに追加する必要があります

[Assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

次に、別のアセンブリでは、公開したいすべてのクラスがあります。 cs_friend_assemblies_2またはアセンブリに名前を付けたすべてのコードについて、intellisenseから基本クラスにアクセスできますが、他の場所にはアクセスできません。

6
Chris Marisic

クラスを継承のために他のアセンブリで同時に利用できるようにすることはできませんが、プライベートにすることもできるので、他のコンシューマーから見ることができません。 [InternalsVisibleTo]属性を使用して、クラスを内部にして特定のアセンブリ(それがフレンドアセンブリの場合)に公開できますが、これはあなたが望むものではないと私は思います。

(派生クラス以外の)コードが基本クラスをインスタンス化できないようにしたい場合は、保護されたコンストラクターを指定できます。

abstract class MyBaseClass
{
    protected MyBaseClass() { ... } // only inheritors can access this...
}

EditorBrowsable属性を使用して、Intellisenseからクラスメンバーを非表示にすることができます。

abstract class MyBaseClass
{ 
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public void SomeMethodToBeHidden() { }
}

一部の人々がIDEの問題を常にこの属性を尊重しているわけではないことを報告したことに注意してください。

4
LBushkin

私に関する限り、これは問題ではありません。観察する:

public abstract class Foo {
    public void virtual Bar() {
        // default implementation
    }
}

public class NormalFoo : Foo { }

public class SpecialFoo : Foo {
    public override void Bar() {
        // special implementation
    }
}

var foolist = new List<Foo>();

foolist.Add( new NormalFoo() );
foolist.Add( new SpecialFoo() );

foreach (var f in foolist) {
    f.Bar();
}

上記は、ポリモーフィズムなしではまったく機能しません-共通のインターフェースである抽象基本クラスを通じて、異なる派生クラスのインスタンスを参照できます。あなたがやりたいことは、それを取り除き、クラス階層の使いやすさを損なうことです。この道を進むべきではないと思います。

1
Joel B Fant

他のアセンブリは、抽象基本クラス、または抽象基本クラスから継承するパブリッククラスから継承しますか?

その場合は、抽象基本クラスをパブリックにする必要があります。不要なメソッドは、アセンブリ内部でのみ表示できます。

そうでない場合は、おそらくインターフェイスが役立ちますか?パブリックインターフェイスを定義し、パブリッククラスにそれらを実装させ、インスタンスを取得するためのファクトリを提供します。このように、インテリセンスがアセンブリの外で見る唯一のものはインターフェースです。

それは役に立ちますか?

0
n8wrl

この制限を回避する方法は、継承の代わりに構成を使用することです(これを行うには other正当な理由 があります)。たとえば、次の代わりに:

internal abstract class MyBase
{
    public virtual void F() {}
    public void G() {}
}

public class MyClass : MyBase // error; inconsistent accessibility
{
    public override void F() { base.F(); /* ... */ }
}

これを行う:

public interface IMyBase
{
    void F();
}

internal sealed class MyBase2 : IMyBase
{
    public void F() {}
    public void G() {}
}

public sealed class MyClass2 : IMyBase
{
    private readonly MyBase2 _decorated = new MyBase2();
    public void F() { _decorated.F(); /* ... */ }
    public void G() { _decorated.G(); }
}

IMyBaseインターフェースを完全に省略して、一般の人がそれを知る必要がなく、内部も知らないようにすることができます。

0
dlf