c# - una - Clase abstracta interna: ¿cómo ocultar el uso fuera del ensamblaje?
nuevo miembro protegido declarado en clase sellada (7)
Tengo un ensamblado / proyecto común que tiene una clase base abstracta, luego varias clases derivadas que deseo hacer públicas a otras asambleas.
No quiero que la clase base abstracta aparezca en estas otras asambleas en Intellisense, así que pensé que sería internal
, pero me sale este error:
Accesibilidad incoherente: la ''configuración'' de la clase base es menos accesible que la clase ''IrcSettings'' ...
Realmente no entiendo esto. Me veo obligado a hacer que la clase abstracta de Settings
public
y, por lo tanto, visible fuera de esta asamblea.
¿Cómo puedo hacer que esta clase sea internal
?
¿Las otras asambleas 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, ¿quizás 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?
Al mismo tiempo, no puede hacer que la clase esté disponible para otros ensamblajes para herencia, sino también para que no sea 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 que el código (que no sean clases derivadas) pueda instanciar su clase base, podría darle un constructor protegido:
abstract class MyBaseClass
{
protected MyBaseClass() { ... } // only inheritors can access this...
}
Puede ocultar los miembros de la clase de Intellisense utilizando el atributo EditorBrowsable
:
abstract class MyBaseClass
{
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public void SomeMethodToBeHidden() { }
}
Cabe señalar que algunas personas han informado de problemas con el IDE que no siempre respetan este atributo.
En lo que a mí respecta, esto no es un problema. Observar:
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, pudiendo referirse a instancias 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.
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.
Realmente no hay mucho beneficio para lo que estás tratando de lograr, pero lo que realmente estás buscando lograr es similar a esto.
Tenga su clase base abstracta en 1 ensamblado con todo lo interno. En AssemblyInfo para ese ensamblaje necesita agregar
[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]
Luego, en otra asamblea, tiene todas las clases que desea que estén disponibles públicamente. 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.
Según entiendo, quiere que su clase abstracta sea implementada solo por otras clases en el mismo ensamblado (por ejemplo, es interna) pero las clases derivadas podrían ser públicas.
La forma de hacerlo es hacer pública la clase base abstracta, pero darle un constructor predeterminado interno:
public abstract class MyClass
{
internal MyClass() { }
}
Esto permitirá que MyClass (y, por lo tanto, sus miembros) sean visibles y utilizables para las clases fuera de su ensamblaje, pero las clases fuera de su ensamblado no pueden heredar de él (se obtendrá un error de compilación).
Editar: si las clases que pueden verse en ensamblajes externos heredan de MyClass, no puede evitar que MyClass también se vea , por ejemplo, aparecer en Intellisense. Sin embargo, puede evitar que se utilicen siguiendo lo anterior.
Una forma de evitar esta limitación es usar composición en lugar de herencia (hay other buenas razones 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(); }
}
Puede omitir la interfaz de IMyBase
completo si el público no necesita saber al respecto y tampoco sus elementos internos.