web-dev-qa-db-fra.com

Classe abstraite interne: comment masquer l'utilisation en dehors de l'assemblage?

J'ai un assembly/projet commun qui a une classe de base abstraite, puis plusieurs classes dérivées que je souhaite rendre publiques aux autres assemblys.

Je ne veux pas que la classe de base abstraite apparaisse dans ces autres assemblys d'Intellisense. Je pensais donc pouvoir le rendre internal, mais j'obtiens cette erreur:

Accessibilité incohérente: la classe de base 'Paramètres' est moins accessible que la classe 'IrcSettings' ....

Je ne comprends pas vraiment ça. Je suis obligé de faire le résumé Settings class public, et donc visible en dehors de cette Assemblée.

Comment puis-je faire cette classe internal à la place?

46
m3ntat

Si j'ai bien compris, vous souhaitez que votre classe abstraite ne soit implémentée que par d'autres classes de la même Assemblée (par exemple, elle est interne), mais les classes dérivées peuvent être publiques.

Pour ce faire, vous devez rendre publique la classe de base abstraite, mais lui attribuer un constructeur interne par défaut:

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

Cela permettra à MyClass (et donc à ses membres) d’être visible et utilisable par les classes extérieures à votre Assemblée, mais les classes extérieures à votre Assemblée ne pourront pas en hériter (il y aura une erreur de compilation).

Éditer: Si les classes que can soient vues par des assemblages externes héritent de MyClass, vous ne pouvez pas empêcher MyClass d’être également seen -, par exemple, s’affichant dans Intellisense. Cependant, vous pouvez les empêcher d'être utilisé en suivant les instructions ci-dessus.

84
Rex M

La classe de base abstraite doit être publique, tout comme la hiérarchie d'héritage d'une classe doit être visible. Cela garantit que le polymorphisme fonctionne et est valide; Cependant, tous les membres des classes de base peuvent être internes (y compris le constructeur), et donc inutilisables en dehors de votre Assembly.

9
thecoop

Ce que vous essayez d'accomplir ne présente pas vraiment d'avantages, mais ce que vous cherchez à réaliser est semblable à ceci.

Avoir votre classe de base abstraite dans 1 Assemblée avec tout interne. Dans l'AssemblyInfo pour cette Assemblée, vous devez ajouter

[Assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

Ensuite, dans une autre assemblée, vous avez toutes les classes que vous voulez publiques. Notez que vous pourrez toujours accéder à la classe de base à partir d’intelliSense pour n’importe quel code situé dans cs_friend_assemblies_2 ou quel que soit le nom que vous donnez à votre Assemblée, mais pas ailleurs.

6
Chris Marisic

Vous ne pouvez pas rendre simultanément la classe disponible à d'autres assemblys pour l'héritage, mais aussi la propriété privée, de sorte qu'elle ne puisse pas être visible pour les autres consommateurs. Vous pouvez rendre la classe interne et l'exposer à un assembly spécifique (s'il s'agit d'un assembly ami) à l'aide de l'attribut [InternalsVisibleTo], mais je ne pense pas que c'est ce que vous voulez.

Si vous souhaitez empêcher le code (autre que les classes dérivées) d'instancier votre classe de base, vous pouvez lui attribuer un constructeur protégé:

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

Vous pouvez masquer les membres de la classe dans Intellisense à l'aide de l'attribut EditorBrowsable:

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

Il convient de noter que certaines personnes ont signalé des problèmes avec le IDE ne respectant pas toujours cet attribut.

4
LBushkin

En ce qui me concerne, il ne s'agit pas d'un problème. Observer:

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

Ce qui précède ne fonctionnerait pas du tout sans polymorphisme - pouvoir faire référence à des instances de différentes classes dérivées via leur interface commune, la classe de base abstraite. Ce que vous voulez faire, c'est enlever cela et paralyser la facilité d'utilisation de votre hiérarchie de classe. Je ne pense pas que vous devriez continuer dans cette voie.

1
Joel B Fant

Les autres assemblys hériteront-ils de votre classe de base abstraite ou des classes publiques qui héritent de votre classe de base abstraite?

Si tel est le cas, vous devez rendre publique la classe de base abstraite. Faites simplement des méthodes que vous ne voulez pas voir visibles en dehors de l’Assemblée.

Si non, peut-être que les interfaces peuvent aider? Définissez les interfaces publiques, faites en sorte que vos classes publiques les implémentent et fournissez une fabrique pour obtenir des instances. De cette manière, intellisense ne voit que l'interface.

Est ce que ça aide?

0
n8wrl

Une façon de contourner cette limitation consiste à utiliser la composition au lieu de l'héritage (il existe autresbonnes raisons pour le faire aussi). Par exemple, au lieu de:

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

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

Faire ceci:

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

Vous pouvez entièrement omettre l’interface IMyBase si le public n’a pas besoin de le savoir et que vos composants internes ne le sont pas non plus.

0
dlf