2009-08-07 45 views
41

Tengo un ensamblado/proyecto común que tiene una clase base abstracta, luego varias clases derivadas que deseo hacer públicas a otros ensambles.Clase abstracta interna: cómo ocultar el uso fuera del ensamblaje?

no quiero la clase base abstracta para aparecer en estas otras asambleas en Intellisense, así que pensé que lo lograría internal, pero me sale este error:

Inconsistent accessibility: base class 'Settings' is less accessible than class 'IrcSettings' ....

no lo hago realmente entiendo esto Estoy obligado a hacer la clase abstracta Settingspublic, y por lo tanto visible fuera de este conjunto.

¿Cómo puedo hacer que esta clase internal en su lugar?

+0

Solo un pequeño punto, puede tener interfaces como tipo de base que es interno para el ensamblaje, pero sus miembros (de la implementación) serán públicos (disponibles fuera del ensamblaje). Si la clase abstracta no tiene ninguna implementación y los miembros son todos públicos, entonces uno debería preferir la interfaz. – nawfal

Respuesta

75

Como yo entiendo, usted quiere que su clase abstracta sea implementada solo por otras clases en el mismo ensamble (ej. es interno) pero las clases derivadas podrían ser públicas.

La manera de hacer esto es hacer que la clase base abstracta pública, pero le dan un constructor por defecto interno:

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

Esto permitirá MiClase (y por lo tanto sus miembros) para que sea visible y utilizable a clases fuera de su ensamblado, pero las clases fuera de su ensamblado no pueden heredar de él (obtendrá un error de compilación).

Editar: Si clases que puede ser visto por los montajes externos heredan de MyClass, no se puede evitar que MiClase de también ser visto - por ejemplo, aparecer en Intellisense. Sin embargo, puede evitar que sean utilizados siguiendo los pasos anteriores.

+3

Mi pregunta no es sobre missue. Ya lo he prevenido. Se trata de no contaminar el Intellisense con una clase que no se puede usar fuera del ensamblaje. – m3ntat

+0

@ m3ntat ver mi respuesta editada. –

+1

Usted _no puede_ hacer que las clases base sean menos accesibles que las clases derivadas. Usted puede hacer que los miembros de la clase base (pero no la clase en sí) sean internos. – thecoop

9

La clase base abstracta debe ser pública, ya que toda la jerarquía de herencia de una clase debe ser visible. Esto asegura que el polimorfismo funciona y es válido; sin embargo, todos los miembros de las clases base pueden ser internos (incluido el constructor) y, por lo tanto, no se pueden usar fuera de su ensamblaje

+2

¿Qué pasa con el voto a favor? – thecoop

+1

A menos que me equivoque, esto no tiene nada que ver con el polimorfismo: una clase puede implementar una interfaz interna, y convertir esa interfaz en una clase abstracta no cambia nada en la forma en que se heredan los métodos, etc., para el caso. Teóricamente, uno podría perfectamente definir una clase que tiene una clase base interna, y usarla en otros conjuntos. Es solo que C# (¿o .NET?) No lo permite. –

6

Realmente no hay mucho beneficio para lo que está tratando de lograr, pero lo que realmente está buscando lograr es similar a esto.

Tenga su clase base abstracta en 1 ensamblaje con todo lo interno. En el AssemblyInfo para que el montaje es necesario agregar

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")] 

Luego, en otro montaje que tiene todas las clases que desee a disposición del público. Tenga en cuenta que aún podrá acceder a la clase base desde intellisense para cualquier código dentro de cs_friend_assemblies_2 o lo que sea que nombre su ensamblaje, pero no en ningún otro lado.

+1

Creo que esta es la única sugerencia para el OP para REALMENTE lograr lo que quiere, aunque este es un método bastante horrible, pero todavía +1 – Letseatlunch

+1

No podría encontrar valor en lo que el OP quiere lograr, pero no diría que esto es un enfoque horrible. Esto básicamente abre "asambleas de amigos" en .NET. Las asambleas de amigos que se usan en situaciones equivocadas pueden generar códigos horribles y marcos terribles, pero eso sería debido a malas prácticas. Creo que el uso más necesario de ensamblajes amigos es para proyectos de prueba y, a veces, en dependencias de ServiceLayer (ex UpdatedBy es {public get; internal protected set} y el Servicio puede asignar UpdatedBy pero en ningún otro lugar lo está). –

+1

me encontré con esta pregunta porque tenía una necesidad similar al OP y encontré que el suyo es el único que realmente funcionaría para el OP. En mi caso, sin embargo, no funcionaría por ciertas razones y esta no era una opción. Así que no llamaría a esto una solución "horrible", es una exageración, pero sí creo que es más como un trabajo pesado que no funcionará en todos los casos. – Letseatlunch

4

No puede hacer que la clase esté disponible simultáneamente para otros conjuntos por herencia, sino también como privada, por lo que no puede ser visible para otros consumidores. Puede hacer que la clase sea interna y exponerla a un ensamblaje específico (si es un ensamblaje amigo) utilizando el atributo [InternalsVisibleTo], pero no creo que esto sea lo que desea.

Si desea mantener el código (aparte de las clases derivadas) de la posibilidad de crear una instancia de la clase base, que podría darle un constructor protected:

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

Puede ocultar los miembros de la clase de Intellisense utilizando el EditorBrowsable atributo:

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

Cabe señalar que algunas personas han reportado problemas con el IDE no siempre respetando este atributo.

+1

EditorBrowsableAttribute solo es válido para los miembros de la clase, no para las declaraciones de clase. –

+0

Esto es lo que estoy buscando, pero no parece respetar esto en Intellisense. Lo intenté. – m3ntat

+0

Además, marcar el resumen de clase evita que se cree una instancia. Tener un constructor predeterminado protegido no. –

0

¿Los otros ensambles alguna vez heredarán de su clase base abstracta o cualquiera de las clases públicas que heredan de su clase base abstracta?

Si es así, debe hacer que la clase base abstracta sea pública. Simplemente haga que los métodos que no desea sean visibles fuera del ensamblaje interno.

Si no, tal vez las interfaces pueden ayudar? Defina interfaces públicas, haga que sus clases públicas las implementen y proporcione una fábrica para obtener instancias. De esa forma, lo único que intellisense ve fuera del ensamblaje es la interfaz.

¿Eso ayuda?

1

Por lo que a mí respecta, esto no es un problema. Observe:

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

Lo anterior no funcionaría en absoluto sin polimorfismo - ser capaz de hacer referencia a los casos de diferentes clases derivadas a través de su interfaz común, la clase base abstracta. Lo que quiere hacer es quitar eso y paralizar la usabilidad de su jerarquía de clases. No creo que debas continuar en este camino.

0

Una forma de evitar esta limitación es utilizar la composición en lugar de la herencia (hay othergood reasons para hacer esto también). Por ejemplo, en lugar 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(); /* ... */ } 
} 

hacer esto:

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

se puede omitir la interfaz IMyBase por completo si el público no tiene por qué saber sobre él y sus partes internas tampoco.