Como chamar base.base.method ()?


126
// Cannot change source code
class Base
{
    public virtual void Say()
    {
        Console.WriteLine("Called from Base.");
    }
}

// Cannot change source code
class Derived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Derived.");
        base.Say();
    }
}

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SpecialDerived sd = new SpecialDerived();
        sd.Say();
    }
}

O resultado é:

Chamado de Special Derived.
Chamado de Derivado. / * isso não é esperado * /
Chamado da Base.

Como posso reescrever a classe SpecialDerived para que o método da classe média "Derived" não seja chamado?

UPDATE: A razão pela qual eu quero herdar da classe Derived em vez de Base é a classe Derived contém muitas outras implementações. Como não posso fazer base.base.method()aqui, acho que a melhor maneira é fazer o seguinte?

// Não é possível alterar o código fonte

class Derived : Base
{
    public override void Say()
    {
        CustomSay();

        base.Say();
    }

    protected virtual void CustomSay()
    {
        Console.WriteLine("Called from Derived.");
    }
}

class SpecialDerived : Derived
{
    /*
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
    */

    protected override void CustomSay()
    {
        Console.WriteLine("Called from Special Derived.");
    }
}

Edição para corresponder à sua atualização.
21410 JoshJordan

Respostas:


106

Só quero adicionar isso aqui, pois as pessoas ainda retornam a essa pergunta mesmo depois de muito tempo. Claro que é uma prática ruim, mas ainda é possível (em princípio) fazer o que o autor deseja com:

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        var ptr = typeof(Base).GetMethod("Say").MethodHandle.GetFunctionPointer();            
        var baseSay = (Action)Activator.CreateInstance(typeof(Action), this, ptr);
        baseSay();            
    }
}

46
Eu realmente gosto dessa resposta porque a pergunta não era se ela é recomendada ou não, ou se é uma boa ideia, mas se havia ou não uma maneira de fazê-lo e, em caso afirmativo, qual é o caminho.
Shavais 9/04/2016

3
Especialmente útil quando você precisa lidar com estruturas / bibliotecas sem extensibilidade. Por exemplo, eu nunca precisei recorrer a essas soluções para o NHibernate, que são altamente extensíveis. Mas, para lidar com o Asp.Net Identity, Entity Framework, Asp.Net Mvc, eu regularmente uso esses hacks para lidar com os recursos ausentes ou comportamentos codificados inadequados para minhas necessidades.
Frédéric

2
Obrigado por isso! Gostaria de mencionar às outras pessoas que parem de citar diretrizes em resposta a uma pergunta. Foi perguntado por uma razão, não por uma bronca. Se você não sabe, não responda! Eu também gostaria de incentivar todos a explodir a polícia de códigos, para que talvez os desanime de postar não respostas. Se, depois de responder a uma pergunta, você se sentir obrigado a citar diretrizes, vá em frente e mencione-a.
DanW

1
E se precisarmos do valor de retorno da função subjacente?
Perkins

2
Deixa pra lá, descobri. Elenco para em Func<stuff>vez deAction
Perkins

92

Essa é uma prática ruim de programação e não é permitida em C #. É uma prática ruim de programação porque

  • Os detalhes da base geral são detalhes de implementação da base; você não deveria confiar neles. A classe base está fornecendo uma cobertura de abstração da base geral; você deve usar essa abstração, não criando um desvio para evitá-lo.

  • Para ilustrar um exemplo específico do ponto anterior: se permitido, esse padrão seria outra maneira de tornar o código suscetível a falhas da classe base frágil. Suponha que Cderiva do Bqual deriva A. Código em Cusos base.basepara chamar um método de A. Então o autor de Bpercebe que eles colocaram muita arte em sala de aula B, e uma abordagem melhor é criar classe intermediária B2que deriva Ae Bderiva B2. Após essa alteração, o code in Cestá chamando um método B2, e não em A, porque Co autor assumiu que os detalhes da implementação B, ou seja, que sua classe base direta éA, nunca mudaria. Muitas decisões de design em C # devem atenuar a probabilidade de vários tipos de falhas na base frágil; a decisão de tornar base.baseilegal impede totalmente esse sabor específico desse padrão de falha.

  • Você derivou de sua base porque gosta do que faz e deseja reutilizá-lo e ampliá-lo. Se você não gosta do que faz e quer contorná-lo em vez de trabalhar com ele, então por que você derivou disso em primeiro lugar? Você mesmo pode derivar da base geral se essa é a funcionalidade que você deseja usar e estender.

  • A base pode exigir certos invariantes para fins de segurança ou consistência semântica, mantidos pelos detalhes de como a base usa os métodos da base principal. Permitir que uma classe derivada da base ignore o código que mantém esses invariantes pode colocar a base em um estado inconsistente e corrompido.


4
@ Jadoon: Em seguida, prefira composição a herança. Sua classe pode usar uma instância do Base ou GrandBase e, em seguida, pode adiar a funcionalidade para a instância.
precisa

4
@BlackOverlord: Como você se sente fortemente sobre esse assunto, por que não escrever uma resposta própria para essa pergunta de sete anos de idade? Dessa forma, todos nós podemos nos beneficiar da sua sabedoria sobre esse assunto e você teria escrito duas respostas no StackOverflow, dobrando sua contribuição total. Isso é ganha-ganha.
Eric Lippert

4
@ Eric Lippert: Existem duas razões pelas quais eu não escrevi minha própria resposta: primeiro, eu não sabia como fazê-lo, por isso encontrei este tópico. Segundo, há uma resposta abrangente de Evk nesta página.
BlackOverlord

3
@ DanW: Eu sei a resposta; é a primeira frase da minha resposta: o recurso desejado não é permitido no C # porque é uma prática ruim de programação . Como você faz isso em c #? Você não A noção de que eu talvez não saiba a resposta para essa pergunta é divertida, mas deixaremos isso passar sem mais comentários. Agora, se você acha esta resposta insatisfatória, por que não escrever sua própria resposta que você acha que faz um trabalho melhor? Dessa forma, todos aprendemos com sua sabedoria e experiência, e você também dobraria o número de respostas que publicou este ano.
precisa

11
@EricLippert, e se o código base for defeituoso e precisar ser substituído como um controle de terceiros? E a implementação inclui uma chamada para grandbase? Para aplicativos do mundo real, pode ser de natureza crítica e precisar de hotfix, portanto, aguardar o fornecedor de terceiros pode não ser uma opção. Má prática versus realidade dos ambientes de produção.
Shiv

22

Você não pode do c #. Na IL, isso é realmente suportado. Você pode fazer uma chamada não-virt para qualquer uma das suas classes principais ... mas, por favor, não faça. :)


11

A resposta (que eu sei que não é o que você está procurando) é:

class SpecialDerived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

A verdade é que você só tem interação direta com a classe da qual herda. Pense nessa classe como uma camada - fornecendo o máximo ou o mínimo dela ou da funcionalidade de seus pais, conforme desejar para suas classes derivadas.

EDITAR:

Sua edição funciona, mas acho que usaria algo assim:

class Derived : Base
{
    protected bool _useBaseSay = false;

    public override void Say()
    {
        if(this._useBaseSay)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Obviamente, em uma implementação real, você pode fazer algo mais parecido com isto para extensibilidade e manutenção:

class Derived : Base
{
    protected enum Mode
    {
        Standard,
        BaseFunctionality,
        Verbose
        //etc
    }

    protected Mode Mode
    {
        get; set;
    }

    public override void Say()
    {
        if(this.Mode == Mode.BaseFunctionality)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Então, as classes derivadas podem controlar o estado de seus pais adequadamente.


3
Por que não escrever apenas uma função protegida na Derivedqual chama Base.Say, para que possa ser chamada SpecialDerived? Mais simples, não?
Nawfal 2/14

7

Por que não simplesmente converter a classe filho para uma classe pai específica e invocar a implementação específica? Essa é uma situação de caso especial e uma solução de caso especial deve ser usada. Você precisará usar a newpalavra - chave nos métodos filhos.

public class SuperBase
{
    public string Speak() { return "Blah in SuperBase"; }
}

public class Base : SuperBase
{
    public new string Speak() { return "Blah in Base"; }
}

public class Child : Base
{
    public new string Speak() { return "Blah in Child"; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Child childObj = new Child();

        Console.WriteLine(childObj.Speak());

        // casting the child to parent first and then calling Speak()
        Console.WriteLine((childObj as Base).Speak()); 

        Console.WriteLine((childObj as SuperBase).Speak());
    }
}

2
Se você tem um mecanismo que não pode saber sobre base ou filho e o speak precisa funcionar corretamente quando chamado por esse mecanismo, o speak precisa ser uma substituição, não um novo. Se o filho precisa de 99% da funcionalidade da base, mas, por um lado, precisa da funcionalidade da superbase ... esse é o tipo de situação que eu entendo que o OP está falando, caso em que esse método não funcionará. Não é incomum e as preocupações de segurança que deram origem ao comportamento do C # geralmente não são muito preocupantes.
Shavais 9/04/16

Apenas uma observação no caso de controles e cadeias de chamadas de eventos, os métodos geralmente são protegidos e, portanto, não são acessíveis assim.
Shiv

2
Isso não está usando herança, você também pode dar a cada Fala um nome totalmente exclusivo.
Nick Sotiros

sim não é 100% herança mas é usando a interface a partir do progenitor
Kruczkowski

5
public class A
{
    public int i = 0;
    internal virtual void test()
    {
        Console.WriteLine("A test");
    }
}

public class B : A
{
    public new int i = 1;
    public new void test()
    {
        Console.WriteLine("B test");
    }
}

public class C : B
{
    public new int i = 2;
    public new void test()
    {
        Console.WriteLine("C test - ");
        (this as A).test(); 
    }
}

3

Você também pode criar uma função simples na classe derivada de primeiro nível, para chamar a função Grand Base


Exatamente isso, e isso preserva todo o esquema de abstração com o qual todos estão tão preocupados, e destaca o modo como os esquemas de abstração às vezes são mais problemáticos do que valem.
Shavais 9/04/16

3

Meu 2c para isso é implementar a funcionalidade que você precisa para ser chamado em uma classe de kit de ferramentas e chamar isso de onde você precisar:

// Util.cs
static class Util 
{
    static void DoSomething( FooBase foo ) {}
}

// FooBase.cs
class FooBase
{
    virtual void Do() { Util.DoSomething( this ); }
}


// FooDerived.cs
class FooDerived : FooBase
{
    override void Do() { ... }
}

// FooDerived2.cs
class FooDerived2 : FooDerived
{
    override void Do() { Util.DoSomething( this ); }
}

Isso requer alguma reflexão sobre o acesso ao privilégio; talvez seja necessário adicionar alguns internalmétodos de acessador para facilitar a funcionalidade.


1

Nos casos em que você não tem acesso à fonte da classe derivada, mas precisa de toda a fonte da classe derivada, além do método atual, recomendo que você também faça uma classe derivada e chame a implementação da classe derivada.

Aqui está um exemplo:

//No access to the source of the following classes
public class Base
{
     public virtual void method1(){ Console.WriteLine("In Base");}
}
public class Derived : Base
{
     public override void method1(){ Console.WriteLine("In Derived");}
     public void method2(){ Console.WriteLine("Some important method in Derived");}
}

//Here should go your classes
//First do your own derived class
public class MyDerived : Base
{         
}

//Then derive from the derived class 
//and call the bass class implementation via your derived class
public class specialDerived : Derived
{
     public override void method1()
     { 
          MyDerived md = new MyDerived();
          //This is actually the base.base class implementation
          MyDerived.method1();  
     }         
}

0

Como pode ser visto em postagens anteriores, pode-se argumentar que, se a funcionalidade da classe precisar ser contornada, algo estará errado na arquitetura da classe. Isso pode ser verdade, mas nem sempre se pode reestruturar ou refatorar a estrutura de classes em um grande projeto maduro. Os vários níveis de gerenciamento de alterações podem ser um problema, mas manter a funcionalidade existente operando da mesma maneira após a refatoração nem sempre é uma tarefa trivial, especialmente se houver restrições de tempo. Em um projeto maduro, pode ser bastante difícil impedir que vários testes de regressão sejam aprovados após uma reestruturação do código; muitas vezes existem "esquisitices" obscuras que aparecem. Tivemos um problema semelhante em alguns casos, a funcionalidade herdada não deve ser executada (ou deve executar outra coisa). A abordagem que seguimos abaixo, foi colocar o código base que precisa ser excluído em uma função virtual separada. Essa função pode ser substituída na classe derivada e a funcionalidade excluída ou alterada. Neste exemplo, o "Texto 2" pode ser impedido de saída na classe derivada.

public class Base
{
    public virtual void Foo()
    {
        Console.WriteLine("Hello from Base");
    }
}

public class Derived : Base
{
    public override void Foo()
    {
        base.Foo();
        Console.WriteLine("Text 1");
        WriteText2Func();
        Console.WriteLine("Text 3");
    }

    protected virtual void WriteText2Func()
    {  
        Console.WriteLine("Text 2");  
    }
}

public class Special : Derived
{
    public override void WriteText2Func()
    {
        //WriteText2Func will write nothing when 
        //method Foo is called from class Special.
        //Also it can be modified to do something else.
    }
}

0

Parece haver muitas dessas questões envolvendo a herança de um método membro de uma classe de avós, substituindo-o em uma segunda classe e, em seguida, chamando seu método novamente de uma classe de neto. Por que não apenas herdar os membros dos avós até os netos?

class A
{
    private string mystring = "A";    
    public string Method1()
    {
        return mystring;
    }
}

class B : A
{
    // this inherits Method1() naturally
}

class C : B
{
    // this inherits Method1() naturally
}


string newstring = "";
A a = new A();
B b = new B();
C c = new C();
newstring = a.Method1();// returns "A"
newstring = b.Method1();// returns "A"
newstring = c.Method1();// returns "A"

Parece simples .... o neto herda o método dos avós aqui. Pense nisso ... é assim que "Object" e seus membros, como ToString (), são herdados para todas as classes em C #. Estou pensando que a Microsoft não fez um bom trabalho ao explicar a herança básica. Há muito foco no polimorfismo e na implementação. Quando vasculho a documentação deles, não há exemplos dessa idéia muito básica. :(


-2

Se você deseja acessar os dados da classe base, deve usar a palavra-chave "this" ou usar essa palavra-chave como referência para a classe.

namespace thiskeyword
{
    class Program
    {
        static void Main(string[] args)
        {
            I i = new I();
            int res = i.m1();
            Console.WriteLine(res);
            Console.ReadLine();
        }
    }

    public class E
    {
        new public int x = 3;
    }

    public class F:E
    {
        new public int x = 5;
    }

    public class G:F
    {
        new public int x = 50;
    }

    public class H:G
    {
        new public int x = 20;
    }

    public class I:H
    {
        new public int x = 30;

        public int m1()
        {
           // (this as <classname >) will use for accessing data to base class

            int z = (this as I).x + base.x + (this as G).x + (this as F).x + (this as E).x; // base.x refer to H
            return z;
        }
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.