usar una sirve que puede para miembros metodos metodo llamar interfaz instancia entre diferencia crear como clases clase caracteristicas abstractos abstractas abstracta c# .net oop

c# - sirve - ¿Cómo forzar la anulación de un método en un descendiente, sin tener una clase base abstracta?



para que sirve una clase abstracta (14)

Creo que estás pensando en esto de la manera incorrecta. Los diseñadores de idiomas no se dijeron a sí mismos "lo que realmente necesitamos es una forma de marcar un método que debe ser anulado , inventemos esto llamado abstracto ". Dijeron: "Un método virtual nos permite representar la idea de que cada tipo derivado de este tipo base debería ser capaz de hacer este método . ¿Pero qué pasa si no hay un código razonable que pueda ir en la versión de la clase base del método? Lo sé , inventemos esto llamado un método abstracto para esa circunstancia ".

Ese es el problema que intentaron resolver los métodos abstractos: usted tiene un método común para todas las clases derivadas pero no una implementación sensata de la clase base, NO "Necesito una forma de forzar a mis tipos derivados a proporcionar una implementación". Que los tipos derivados se ven obligados a proporcionar una implementación es una consecuencia de la solución , pero no es el problema que se pretende resolver en primer lugar.

El lenguaje C # no tiene un mecanismo para el problema "Debo forzar a mi subtipo a proporcionar su propia implementación de este método" porque no es un problema que los diseñadores de idiomas, que yo sepa, alguna vez consideraron un problema para la mayoría de nuestros clientes.

Entonces mi pregunta es: ¿por qué quieres hacer esto? Seguramente le corresponde al desarrollador de la clase derivada determinar si la implementación de la clase base es correcta o no para la clase derivada. Eso no depende de ti. E incluso si tuvieras alguna forma de hacerlo, ¿qué impediría que el desarrollador simplemente dijera

override void M() { base.M(); }

?

¿Puede explicar qué propósito tiene para intentar forzar este trabajo a los desarrolladores de sus clases derivadas? Tal vez hay una mejor manera de lograr lo que quieres.

Pero, en términos más generales, no estoy seguro de que su jerarquía esté razonablemente diseñada en primer lugar. Cuando veo un método GiveBonus en un empleado, supongo que esto significa que "un empleado puede dar un bono", no "un empleado puede recibir un bono". Seguramente un gerente da una bonificación y un empleado recibe una bonificación. Creo que podrías hacer que la jerarquía de empleados haga demasiado.

El encabezado de la pregunta parece ser un poco confuso, pero trataré de aclarar mi pregunta aquí.

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { public abstract class Employee { private string name; private int empid; BenefitPackage _BenefitPackage = new BenefitPackage(); public string Name { get { return this.name; } set { this.name = value; } } public int EmpId { get { return this.empid; } set { if (value == 1) return; this.empid = value; } } public Employee(string Name, int EmpId) { this.Name = Name; this.EmpId = EmpId; } public Employee() { } public abstract void GiveBonus(); } public class Manager : Employee { private int noofstockoptions; public override void GiveBonus() { Console.WriteLine("Manger GiveBonus Override"); } public int NoOfStockOptions { get { return this.noofstockoptions; } set { this.noofstockoptions = value; } } public Manager(string Name,int EmpId, int NoOfStockOptions):base(Name,EmpId) { this.NoOfStockOptions=NoOfStockOptions; } } public class SalesPerson:Employee { private int noofsales; public int NoOfSales { get { return this.noofsales; } set { this.noofsales = value; } } public SalesPerson(string Name, int EmpId, int NoOfSales):base(Name,EmpId) { this.NoOfSales = NoOfSales; } public override void GiveBonus() { Console.WriteLine("Hi from salesperson"); } } public sealed class PTSalesPerson : SalesPerson { private int noofhrworked; public int NoOfHrWorked { get { return this.noofhrworked; } set { this.noofhrworked = value; } } public PTSalesPerson(string Name, int EmpId, int NoOfSales,int NoOfHrWorked):base(Name,EmpId,NoOfSales) { this.NoOfHrWorked = NoOfHrWorked; } //public new void GiveBonus() //{ // Console.WriteLine("hi from ptsalesperson"); //} } class BenefitPackage { public int Bonus; public int GiveBonus() { int i = 200; return i; } private class innerPublic { public int innerBonus; } } class MainClass { public static void Main() { Manager _Manager=new Manager("Vaibhav",1,50); PTSalesPerson _PTSalesPerson = new PTSalesPerson("Shantanu", 1, 4, 6); _Manager.GiveBonus(); Employee _emp; //_emp = new Employee("new emp",4); //_emp.GiveBonus(); _PTSalesPerson.GiveBonus(); ((SalesPerson)_PTSalesPerson).GiveBonus(); Console.ReadLine(); } } }

Por favor, no intentes comprender todo el código. Lo estoy resumiendo.

  1. Empleado es una clase abstracta, que tiene un método abstracto GiveBonus
  2. SalesPerson es un derivado de Employee. SalesPerson tiene que dar una definición al método abstracto GiveBonus. ( SalesPerson no puede ser abstracto )
  3. PTSalesPerson se deriva de SalesPerson.

Ahora mi pregunta es, ¿cómo puedo forzar a PTSalesPerson a tener su propia implementación de GiveBonus?


De acuerdo, sé que esta publicación es antigua, sin embargo, recientemente tuve que buscar esta respuesta, así que para cualquier otra persona que busque esto: (Estoy usando VS .net .net 4.5, así que no estoy seguro de las versiones anteriores)

Creé una clase abstracta y usé el reemplazo en ambas clases secundarias:

public abstract class Person { public string Name { get; protected set; } public abstract void GiveName(string inName); } public class Employee : Person { public override void GiveName(string inName) { Name = inName; } } public class SalesPerson:Employee { public override void GiveName(string inName) { Name = "Sales: "+inName; } }

Pruebas:

SalesPerson x = new SalesPerson(); x.GiveName("Mark"); //Name="Sales: Mark" Employee e = x; e.GiveName("Mark"); //Name="Sales: Mark" Employee m = new Employee(); m.GiveName("Mark"); //Name="Mark"


Declare la clase Employee como abstracto, pero proporcione e implemente GiveBonus() que arroje una excepción de tiempo de ejecución con un mensaje como "Debe ser implementado por subclases" . Es una vieja práctica de Smalltalk ... No estoy seguro de si es útil para C #. Lo he usado en código Java.


Espero sinceramente que mi respuesta ayude a algunas personas confundidas por este problema.

Tenga paciencia conmigo, pero intentaré volver a resumir lo que se me pide, solo para asegurarme de que estoy respondiendo la pregunta correcta. ¡Entonces daré la respuesta!

Creo que la esencia de la pregunta es: ¿cómo declaro un método en una clase abstracta para que tenga una implementación pero todavía se requiere una clase derivada para anular el método?

C # no parece ser compatible con esto. Si declara el método ''abstracto'', no está permitido tener una implementación (cuerpo). Pero si declara que es "virtual", una clase derivada no está obligada a anularla. C # no permite que un método se marque como abstracto y virtual.

Creo que hay muchas situaciones en las que deseas hacer esto (lo que se está preguntando).

La forma de resolver este enigma es la siguiente: ¡Declara dos métodos! Uno de ellos está marcado como "abstracto"; el otro está marcado como "virtual" y llama al primero en algún lugar de su cuerpo.

De esta forma, la clase derivada se ve obligada a anular el método abstracto, pero la implementación (parcial) en el método virtual puede heredarse de manera segura.

Entonces, por ejemplo, el método de empleado GiveBonus podría declararse así:

public abstract decimal ComputeBonus(); public virtual void GiveBonus() { decimal amount = ComputeBonus(); if (amount > 0.0) PostBonus(amount); }

Dejaré los detalles de PostBonus a la imaginación, pero la belleza de este enfoque es que las clases derivadas se ven obligadas a anular ComputeBonus, pero GiveBonus se beneficia de la implementación parcial proporcionada en la clase base. HTH


Este es un hilo muy antiguo, pero la respuesta dada en esta pregunta puede ser útil. Puede obligar a una clase derivada a tener su propia implementación de un método / propiedad virtual.

public class D { public virtual void DoWork(int i) { // Original implementation. } } public abstract class E : D { public abstract override void DoWork(int i); } public class F : E { public override void DoWork(int i) { // New implementation. } }

Si un método virtual se declara abstracto, sigue siendo virtual para cualquier clase que herede de la clase abstracta. Una clase que hereda un método abstracto no puede acceder a la implementación original del método; en el ejemplo anterior, DoWork en la clase F no puede llamar a DoWork en la clase D. De esta forma, una clase abstracta puede forzar clases derivadas para proporcionar nuevas implementaciones de métodos para métodos virtuales .


Esto es bastante viejo, pero tenemos una situación algo similar. La clase base carga un archivo de configuración y configura los valores predeterminados de la clase base. Sin embargo, el archivo de configuración también podría contener valores predeterminados para las clases heredadas.

Así es como obtuvimos la funcionalidad combinada.

Métodos de clase base

private void processConfigurationFile() { // Load and process the configuration file // which happens to be an xml file. var xmlDoc = new XmlDocument(); xmlDoc.Load(configurationPath); // This method is abstract which will force // the inherited class to override it. processConfigurationFile(xmlDoc); } protected abstract void processConfigurationFile(XmlDocument document);


La única manera que puedo ver de hacer que esto funcione, si no puedes hacer SalesPerson Abstract, es este:

1) en SalesPerson.GiveBonus (...) use la reflexión para determinar si ''esto'' es un SalesPerson, o una clase derivada a) si no es una clase derivada, haga el código actual en SalesPerson.GiveBonus b) de lo contrario, llame a GiveBonusDerived. (declare esto como virtual y haga que la implementación en SalesPerson genere una excepción).

Los inconvenientes aquí son, la reflexión es lenta. No hay error de tiempo de compilación si GiveBonusDerived no está declarado, etc.


No puede usar la configuración que describió. PTSalesPerson ya tendrá una implementación de GiveBonus porque hereda de SalesPerson.


No puede, a menos que haga SalesPerson abstracto o cambie la jerarquía.

Qué tal si:

Employee* ^ | SalesPersonBase* (have all the code except GiveBonus) ^ ^ | | SalesPerson PTSalesPerson

Tanto Employee como SalesPersonBase ahora están marcados como abstractos.

Sin embargo, si necesita un PTSalesPerson para no solo heredar el comportamiento, sino también heredar la relación is-a (un PTSalesPerson también es un SalesPerson), entonces no tiene forma de forzar esto.

Tenga en cuenta que el texto anterior solo es válido si solo considera las comprobaciones en tiempo de compilación. En otras palabras, si quiere que el compilador se queje si no ha agregado una anulación a la clase PTSalesPerson, no puede hacer eso, a menos que haga lo que describí anteriormente.

Sin embargo, no hay nada que le impida utilizar el reflejo para examinar los métodos en tiempo de ejecución, y lanzar una excepción si el método en PTSalesPerson no se reemplaza explícitamente allí, sin embargo, yo consideraría que es un hack.


Si no va a crear una instancia de SalesPerson directamente, entonces lo más simple es hacer que SalesPerson sea abstracto y eso obligaría a las clases secundarias a implementarlo en su lugar (o a ser abstractas).

Si hay alguna lógica en ese método común para todos los SalesPeople, entonces podría implementar GiveBonus en SalesPerson como un método de plantilla . Lo que requiere algún método abstracto requerido en cualquier subclase:

De cualquier forma que decida, la única forma de forzar una implementación en una clase infantil es hacer que la clase base sea abstracta.


Siempre puede hacer que la implementación de SalesPerson arroje una NotImplementedException. : V Pero contractualmente, no, no puedes hacer eso.


Siento que esto indica que su implementación realmente tiene dos partes: una implementación parcial que va en la clase base, y una finalización faltante para esa implementación que desea en la subclase. Ex:

public abstract class Base { public virtual void PartialImplementation() { // partial implementation } } public sealed class Sub : Base { public override void PartialImplementation() { base.PartialImplementation(); // rest of implementation } }

Desea forzar la anulación, ya que la implementación inicial está incompleta. Lo que puede hacer es dividir su implementación en dos partes: la parte parcial implementada y la parte faltante de la implementación en espera. Ex:

public abstract class Base { public void PartialImplementation() { // partial implementation RestOfImplementation(); } // protected, since it probably wouldn''t make sense to call by itself protected abstract void RestOfImplementation(); } public sealed class Sub : Base { protected override void RestOfImplementation() { // rest of implementation } }


Un WA puede estar usando una "Interfaz" para que pueda definir uno como IBonusGiver {void GiveBonus (); } Entonces, en lugar del método abstracto y las anulaciones, implementa en todas sus clases esta nueva interfaz. por ejemplo, PTSalesPerson: SalesPerson, IBonusGiver forzando una nueva implementación en cada clase.


Use inyección de dependencia. Crear una clase BonusCalculator :

public abstract class BonusCalculator { public abstract decimal CalculateBonus(Employee e) }

En tu clase base:

private BonusCalculator Calculator { get; set; } public void GiveBonus() { Bonus = Calculator.CalculateBonus(this) }

En el constructor de su implementación:

public SomeKindOfEmployee() { Calculator = new SomeKindOfEmployeeBonusCalculator(); }

Alguien que implementa una subclase Person ahora tiene que proporcionarle explícitamente una instancia de BonusCalculator (u obtener una NullReferenceException en el método GiveBonus ).

Como una bonificación adicional, este enfoque permite que diferentes subclases de Person compartan un método de cálculo de bonificación si es apropiado.

Editar

Por supuesto, si PTSalesPerson deriva de SalesPerson y su constructor llama al constructor base, tampoco funcionará.