Pregunta

¿Cómo declara un método en C # que debe ser anulado (o anulable) por una clase descartada, posiblemente incluso fuera de su ensamblaje, pero que debería ser invocable solo dentro de la clase real?

(es decir, como una función virtual privada en C ++)

[editar]
private virtual es exactamente lo que pretendo: " Esta es una forma de modificar mi comportamiento, pero aún no se te permite llamar a esta función directamente (porque llamarlo requiere invocaciones arcanas que solo mi clase base debe hacer) "

Entonces, para aclarar: ¿cuál es la mejor expresión para eso en C #?

¿Fue útil?

Solución

C # hace una garantía más fuerte para " privado " que C ++ hace. En C ++, puede anular un método virtual privado. Pero eso significa que el código en una clase base puede ejecutar código en una clase derivada. Romper la promesa de que el método privado es verdaderamente privado y solo puede llamarse por métodos de la misma clase.

Algo que no ayuda aquí es que C ++ no requiere repetir la palabra clave virtual. Liderando misterios difíciles de revertir como este:

#include "stdafx.h"
#include <iostream>

class Base {
private:
    virtual void Method() = 0;
public:
    void Test() {
        Method();
    }
};
class Derived : public Base {
private:
    void Method() { std::cout << "Who the heck called me?"; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Base* p = new Derived;
    p->Test();
}

Estoy de acuerdo en que hay un posible papel para la herencia privada. Los diseñadores de lenguaje C # dijeron ¡No! aunque.

Otros consejos

Cuando dices que solo debería ser invocable " dentro de la clase real " ¿Quieres decir la clase base o la clase derivada? Ninguno de estos es factible por sí solo. Lo más cercano es usar un método protegido, lo que significa que se puede llamar desde la clase declarante, la clase derivada y cualquier otra clase derivada.

Un miembro privado no es visible para las clases secundarias. Creo que el virtual protegido se realizará de la manera que te gustaría?

ACTUALIZAR:

Aquí, con mayor detalle, se explica lo que puede hacer con las funciones de herencia y reemplazo dentro de C #. Intenté usar un ejemplo significativo, pero considero que es un diseño de clase pobre y nunca recomendaría implementar las clases descritas de esta manera. Sin embargo, espero que esto le brinde la oportunidad de abordar su problema original de una manera que sea aceptable. No hay manera de evitar que una clase concreta llame a cualquiera de sus miembros, pero si su estructura es así de todas formas, tal vez no sea un problema.

public abstract class Animal
{
    public void DisplayAttributes()
    {
        Console.WriteLine(Header());
        Console.WriteLine("Name: " + Name());
        Console.WriteLine("Legs: " + Legs());
        Console.WriteLine();
    }

    protected virtual int Legs()
    {
        return 4;
    }

    private string Header()
    {
        return "Displaying Animal Attributes";
    }

    protected abstract string Name();
}

public class Bird : Animal
{
    protected override string Name()
    {
        return "Bird";
    }

    protected override int Legs()
    {
        return 2;
    }
}

public class Zebra : Animal
{
    protected override string Name()
    {
        return "Zebra";
    }
}

public class Fish : Animal
{
    protected override string Name()
    {
        return "Fish";
    }

    protected override int Legs()
    {
        return 0;
    }

    private string Header()
    {
        return "Displaying Fish Attributes";
    }

    protected virtual int Gils()
    {
        return 2;
    }

    public new void DisplayAttributes()
    {
        Console.WriteLine(Header());
        Console.WriteLine("Name: " + Name());
        Console.WriteLine("Gils: " + Gils());
        Console.WriteLine();
    }
}

class Program
{
    static void Main(string[] args)
    {
        Bird bird = new Bird();
        bird.DisplayAttributes();
        //Displaying Animal Attributes
        //Name: Bird
        //Legs: 2

        Zebra zebra = new Zebra();
        zebra.DisplayAttributes();
        //Displaying Animal Attributes
        //Name: Zebra
        //Legs: 4


        Fish fish = new Fish();
        fish.DisplayAttributes();
        //Displaying Fish Attributes
        //Name: Fish
        //Gils: 2

        List<Animal> animalCollection = new List<Animal>();
        animalCollection.Add(bird);
        animalCollection.Add(zebra);
        animalCollection.Add(fish);

        foreach (Animal animal in animalCollection)
        {
            animal.DisplayAttributes();
            //Displaying Animal Attributes
            //Name: Bird
            //Legs: 2

            //Displaying Animal Attributes
            //Name: Zebra
            //Legs: 4

            //Displaying Animal Attributes
            //Name: Fish
            //Legs: 0
            //*Note the difference here
            //Inheritted member cannot override the
            //base class functionality of a non-virtual member
        }
    }
}

En este ejemplo, Bird, Zebra y Fish podrían llamar a sus métodos de Nombre y Piernas, pero dentro del contexto, si este ejemplo, no necesariamente sería útil hacerlo. Además, como lo muestra Fish, DisplayAttributes () se puede modificar para una instancia de una clase derivada concreta; pero cuando estás mirando a un Animal, como en el bucle foreach, obtienes el comportamiento DisplayAttributes de las clases base, sin importar el tipo real de animal. Espero que esto pueda ayudar a proporcionar el tipo de funcionalidad que le gustaría replicar.

¿Consideraste el uso de un delegado para hacer eso? Puede permitir que la clase derivada establezca el delegado a través de alguna propiedad protegida o pasándolo a su constructor. También puede predeterminar al delegado a su implementación interna, que es un método privado en su clase base.

Aquí hay un ejemplo de lo que vboctor ya ha mencionado:

public class Base
{
    private Func<Base, int> func;
    protected void SetFunc(Func<Base, int> func)
    {
        this.func = func;
    }

    private void CallFunc()
    {
        if (func != null)
        {
            var i = func(this);
        }
    }
}

public class Sub : Base
{
    private void DoFuncyStuff()
    {
         this.SetFunc(b => 42);
    }
}

¿Por qué necesitas que sea privado? Protegido debería ser suficiente, aquí. Le estás pidiendo al autor de la subclase que escriba un código al que no puedan llamar. ¿Qué logra esto? Podrían usar ese código de todos modos.

Mientras leo tu pregunta, podrías decir dos cosas.

Primero, si desea una función en la Clase A que puede ser anulada en la Clase B del Niño pero no es visible para ninguna clase externa:

public class ClassA
{
  protected virtual ReturnType FunctionName(...) { ... }
}

public class ClassB
{
  protected override ReturnType FunctionName(...) { ... }
}

En segundo lugar, si desea forzar a una clase implementadora a definir la función:

public abstract class ClassA
{
  protected abstract ReturnType FunctionName(...);
}

public class ClassB
{
  protected override ReturnType FunctionName(...) { ... }
}

Otro concepto que podría considerar si solo está investigando en C # que está relacionado es clases parciales. Esta es la idea de dos archivos de origen que se combinan en el momento de la compilación para crear una clase, ambos del mismo ensamblado:

Archivo 1:

public partial class ClassA
{
    private ReturnType FunctionName(...);
}  

Archivo 2:

public partial class ClassA
{
  //actual implimentation
  private ReturnType FunctionName(...){ ... }; 
}

Los parciales no se usan ampliamente, excepto cuando se trata de archivos generados diseñados, como los archivos Linq2Sql, EDM, WinForms, etc.

Supongo que esto no funcionará como pretendías, pero déjame dibujar un pseudo-código para ti:

public interface BaseClassFunction {
    void PleaseCallMe();
}

public class BaseClass {
    private BaseClassFunction fn;
    public BaseClass(BaseClassFunction fn) {
        this.fn = fn;
    }
    private CallMe() {
        fn.PleaseCallMe();
    }
    public PublicCallMe() {
        CallMe();
    }
}

private class DerivedClassFunction : BaseClassFunction {
    void PleaseCallMe() { ... do something important ... }
}

public class DerivedClassFunction {
    public DerivedClassFunction() : BaseClass(new DerivedClassFunction()) {
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top