polimorfismo polimorfico metodos herencia form derivada clases clase abstracta c# class derived

polimorfico - C#- ¿Pueden ocultarse métodos públicamente heredados(por ejemplo, privados a la clase derivada)?



polimorfismo c# (10)

Supongamos que tengo BaseClass con los métodos públicos A y B, y creo DerivedClass a través de la herencia.

p.ej

public DerivedClass : BaseClass {}

Ahora quiero desarrollar un método C en DerivedClass que use A y B. ¿Hay alguna forma de que pueda anular los métodos A y B para que sean privados en DerivedClass, de modo que solo el método C quede expuesto a alguien que quiera usar mi DerivedClass?


@Brian R. Bondy me señaló un interesante artículo sobre Ocultar a través de la herencia y la nueva palabra clave.

http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx

Entonces, como solución alternativa sugeriría:

class BaseClass { public void A() { Console.WriteLine("BaseClass.A"); } public void B() { Console.WriteLine("BaseClass.B"); } } class DerivedClass : BaseClass { new public void A() { throw new NotSupportedException(); } new public void B() { throw new NotSupportedException(); } public void C() { base.A(); base.B(); } }

De esta forma, un código como este arrojará una NotSupportedException :

DerivedClass d = new DerivedClass(); d.A();


Cuando, por ejemplo, intenta heredar de una List<object> y desea ocultar el miembro de Add(object _ob) :

// the only way to hide [Obsolete("This is not supported in this class.", true)] public new void Add(object _ob) { throw NotImplementedException("Don''t use!!"); }

No es realmente la solución más preferible, pero cumple su función. Intellisense todavía acepta, pero en tiempo de compilación obtiene un error:

error CS0619: ''TestConsole.TestClass.Add (TestConsole.TestObject)'' está obsoleto: ''Esto no es compatible en esta clase''.


Diría que si tienes una base de código con la que deseas hacer esto, no es la base de código mejor diseñada. Por lo general, es un signo de una clase en un nivel de la jerarquía que necesita una determinada firma pública, mientras que otra clase derivada de esa clase no la necesita.

Un próximo paradigma de codificación se llama "Composición sobre herencia". Esto juega directamente fuera de los principios de desarrollo orientado a objetos (especialmente el Principio de Responsabilidad Individual y el Principio Abierto / Cerrado).

Desafortunadamente, la forma en que a muchos de nosotros los desarrolladores se les enseñó la orientación a objetos, nos hemos formado el hábito de pensar inmediatamente en la herencia en lugar de en la composición. Tendemos a tener clases más grandes que tienen muchas responsabilidades diferentes simplemente porque podrían estar contenidas con el mismo objeto "Mundo real". Esto puede conducir a jerarquías de clase que tienen más de 5 niveles de profundidad.

Un desafortunado efecto colateral que los desarrolladores normalmente no tienen en cuenta al lidiar con la herencia es que la herencia forma una de las formas más fuertes de dependencias que usted puede introducir en su código. Su clase derivada ahora depende en gran medida de la clase desde la que fue heredada. Esto puede hacer que su código sea más frágil a largo plazo y generar problemas confusos en los que cambiar un determinado comportamiento en una clase base rompe las clases derivadas de manera oscura.

Una forma de romper el código es mediante interfaces como las mencionadas en otra respuesta. Esto es algo inteligente de hacer de todos modos, ya que desea que las dependencias externas de una clase se vinculen a abstracciones, no tipos concretos / derivados. Esto le permite cambiar la implementación sin cambiar la interfaz, todo sin afectar una línea de código en su clase dependiente.

Preferiría mucho más que mantener un sistema con cientos / miles / incluso más clases que son todas pequeñas y poco compactas, que lidiar con un sistema que hace un uso intensivo de polimorfismo / herencia y tiene menos clases que están más estrechamente conectadas.

Quizás el mejor recurso que existe sobre desarrollo orientado a objetos es el libro de Robert C. Martin, Desarrollo de software ágil, Principios, patrones y prácticas .


Esconderse es una pendiente bastante resbaladiza. Los principales problemas, la OMI, son:

  • Depende del tipo de declaración de tiempo de diseño de la instancia, lo que significa que si hace algo como BaseClass obj = new SubClass (), luego llama a obj.A (), se oculta el ocultamiento. BaseClass.A () se ejecutará.

  • Ocultar puede oscurecer muy fácilmente el comportamiento (o los cambios de comportamiento) en el tipo de base. Obviamente, esto es menos preocupante cuando tienes ambos lados de la ecuación, o si llamar a ''base.xxx'' es parte de tu sub-miembro.

  • Si realmente posee ambos lados de la ecuación de base / subclase, entonces debería ser capaz de idear una solución más manejable que la ocultación / ocultación institucionalizada.

Eso suena como una mala idea. Liskov no estaría impresionado.

Si no desea que los consumidores de DerivedClass puedan acceder a los métodos DeriveClass.A () y DerivedClass.B (), sugeriría que DerivedClass implemente alguna interfaz pública IWhateverMethodCIsAbout y los consumidores de DerivedClass realmente se comuniquen con IWhateverMethodCIsAbout y conozcan nada sobre la implementación de BaseClass o DerivedClass en absoluto.


La única forma de hacer esto que yo sepa es usar una relación Has-A y solo implementar las funciones que desea exponer.


Si bien la respuesta a la pregunta es "no", hay un consejo que deseo señalar para los demás que lleguen aquí (dado que el OP fue como aludir al acceso al ensamblaje por parte de terceros). Cuando otros hacen referencia a un ensamblado, Visual Studio debe respetar el siguiente atributo para que no se muestre en intellisense (oculto, pero PUEDE ser llamado, así que tenga cuidado):

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

Si no tuviera otra opción, debería poder usar la opción new en un método que oculte un método de tipo base, return => throw new NotSupportedException(); y combinarlo con el atributo anterior.

Otro truco depende de NO heredar de una clase base si es posible, donde la base tiene una interfaz correspondiente (como IList<T> para List<T> ). La implementación de interfaces "explícitamente" también ocultará esos métodos de intellisense en el tipo de clase. Por ejemplo:

public class GoodForNothing: IDisposable { void IDisposable.Dispose() { ... } }

En el caso de var obj = new GoodForNothing() , el método Dispose() no estará disponible en obj . Sin embargo, ESTARÁ disponible para cualquier persona que IDisposable explícitamente obj a IDisposable .

Además, también puede ajustar un tipo de base en lugar de heredar de él, y luego ocultar algunos métodos:

public class MyList<T> : IList<T> { List<T> _Items = new List<T>(); public T this[int index] => _Items[index]; public int Count => _Items.Count; public void Add(T item) => _Items.Add(item); [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden) /*...etc...*/ }


Si se definen como públicos en la clase original, no puede anularlos para que sean privados en su clase derivada. Sin embargo, puede hacer que el método público genere una excepción e implemente su propia función privada.

Editar: Jorge Ferreira está en lo correcto.


Lo que necesitas es composición, no herencia.

class Plane { public Fly() { .. } public string GetPilot() {...} }

Ahora, si necesitas un tipo especial de avión, como uno que tenga PairOfWings = 2, pero que haga todo lo que un avión pueda hacer ... Heredas el plano. Con esto declaras que tu derivación cumple con el contrato de la clase base y puede ser sustituida sin parpadear donde se espera una clase base. por ejemplo, LogFlight (Plano) continuaría funcionando con una instancia de BiPlane.

Sin embargo, si solo necesitas el comportamiento Fly para un nuevo Bird que quieras crear y no estás dispuesto a admitir el contrato completo de la clase base, en su lugar compones. En este caso, refactorice el comportamiento de los métodos para reutilizar en un nuevo tipo Vuelo. Ahora crea y mantén referencias a esta clase en Plane y Bird. No heredas porque Bird no admite el contrato completo de la clase base ... (p. Ej., No puede proporcionar GetPilot ()).

Por la misma razón, no puede reducir la visibilidad de los métodos de la clase base cuando anula ... puede anular y hacer que un método privado base sea público en la derivación, pero no al revés. Por ejemplo, en este ejemplo, si obtengo un tipo de plano "BadPlane" y luego anulo y "Ocultar" GetPilot (), lo hago privado; un método cliente LogFlight (Plano p) funcionará para la mayoría de los aviones pero explotará para "BadPlane" si la implementación de LogFlight necesita / llama a GetPilot (). Dado que se espera que todas las derivaciones de una clase base sean "sustituibles" siempre que se espere un parámetro de clase base, esto tiene que ser rechazado.


No es posible, ¿por qué?

En C #, se le impone que si hereda métodos públicos, debe hacerlos públicos. De lo contrario, esperan que no se derive de la clase en primer lugar.

En lugar de usar la relación is-a, tendrías que usar la relación has-a.

Los diseñadores de idiomas no permiten esto a propósito para que pueda usar la herencia de forma más adecuada.

Por ejemplo, uno podría confundir accidentalmente un automóvil de clase para derivar de un motor de clase para obtener su funcionalidad. Pero un motor es la funcionalidad que usa el automóvil. Entonces querrías usar la relación has-a. El usuario del automóvil no quiere tener acceso a la interfaz del motor. Y el auto en sí no debe confundir los métodos del motor con los suyos. Las derivaciones futuras de Nor Car.

Por lo tanto, no permiten que lo proteja de las malas jerarquías de herencia.

¿Qué deberías hacer en su lugar?

En su lugar, debe implementar interfaces. Esto te deja libre para tener funcionalidad usando la relación has-a.

Otros idiomas:

En C ++, simplemente especifica un modificador antes de la clase base de privado, público o protegido. Esto hace que todos los miembros de la base que fueron públicos para ese nivel de acceso especificado. Me parece tonto que no puedas hacer lo mismo en C #.

El código reestructurado:

interface I { void C(); } class BaseClass { public void A() { MessageBox.Show("A"); } public void B() { MessageBox.Show("B"); } } class Derived : I { public void C() { b.A(); b.B(); } private BaseClass b; }

Entiendo que los nombres de las clases anteriores son un poco discutibles :)

Otras sugerencias:

Otros han sugerido hacer públicos A () y B () y lanzar excepciones. Pero esto no crea una clase amigable para que las personas lo usen y no tiene sentido.