переопределить, но не звонить - PullRequest
2 голосов
/ 06 января 2009

Как вы объявляете метод в C #, который должен быть переопределен (или переопределен) разыскиваемым классом - возможно, даже вне вашей сборки - но который должен вызываться только из фактического класса?

(т.е. как частная виртуальная функция в C ++)

[править]
private virtual - это именно то, что я намереваюсь: «Вот способ изменить мое поведение, но вы все равно не можете напрямую вызывать эту функцию (потому что для ее вызова требуются тайные вызовы, которые есть только у моего базового класса должен делать) "

Итак, чтобы уточнить это: каково лучшее выражение для этого в C #?

Ответы [ 8 ]

3 голосов
/ 06 января 2009

Когда вы говорите, что он должен вызываться только «внутри фактического класса», вы имеете в виду базовый класс или производный класс? Ни один из них сам по себе невозможен. Наиболее близким является использование защищенного метода, что означает, что его можно вызывать из объявленного класса, производного класса и любого другого производного класса.

2 голосов
/ 06 января 2009

Закрытый член не виден дочерним классам. Я думаю, защищенный виртуал будет работать так, как вы хотите?

UPDATE:

Здесь более подробно объясняется, что вы можете делать с функциями наследования и переопределения в C #. Я попытался использовать несколько содержательный пример, но считаю, что он понимает, что это плохой дизайн классов, и я бы никогда не рекомендовал реализовывать классы, описанные таким образом. Тем не менее, я надеюсь, что, возможно, это даст вам возможность приблизиться к решению вашей первоначальной проблемы таким образом, чтобы это было приемлемо. Нет никакого способа предотвратить конкретный класс от вызова любого из его членов, но если ваша структура в любом случае такова, возможно, это не проблема.

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
        }
    }
}

В этом примере Bird, Zebra и Fish могут вызывать свои методы Name и Legs, но в этом контексте контекст не обязательно будет полезен для этого. Кроме того, как показано в Fish, DisplayAttributes () может быть изменен для экземпляра конкретного производного класса; но когда вы смотрите на Animal, как в цикле foreach, вы получаете поведение базовых классов DisplayAttributes независимо от фактического типа животного. Я надеюсь, что это может помочь определить тип функций, которые вы хотели бы воспроизвести.

1 голос
/ 27 марта 2009

Вот пример того, что vboctor уже упоминал:

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);
    }
}
1 голос
/ 06 января 2009

Рассматривали ли вы использование делегата для этого? Вы можете разрешить производному классу устанавливать делегат через какое-либо защищенное свойство или передавая его своему конструктору. Вы также можете по умолчанию делегировать вашу внутреннюю реализацию, которая является закрытым методом в вашем базовом классе.

1 голос
/ 06 января 2009

C # дает более сильную гарантию для «частного», чем C ++. В C ++ вы действительно можете переопределить закрытый виртуальный метод. Но это означает, что код в базовом классе может выполнять код в производном классе. Нарушение обещания, что закрытый метод является действительно закрытым и может вызываться только методами в том же классе.

Здесь не помогает то, что C ++ не требует повторения ключевого слова virtual. Приводя к трудным, чтобы перепроектировать загадки как это:

#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();
}

Я согласен, что возможна роль частного наследования. Разработчики языка C # сказали нет! хотя.

0 голосов
/ 06 января 2009

Думаю, это не сработает так, как вы хотели, но позвольте мне набросать для вас псевдокод:

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()) {
    }
}
0 голосов
/ 06 января 2009

Когда я читаю ваш вопрос, вы можете иметь в виду две вещи.

Во-первых, если вам нужна функция в классе A, которая может быть переопределена в дочернем классе B, но невидима для любого внешнего класса:

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

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

Во-вторых, если вы хотите заставить реализующий класс определить функцию:

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

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

Другая концепция, на которую вы могли бы взглянуть, если вы просто копаетесь в C #, которая в некотором роде связана с частичными классами. Это идея объединения двух исходных файлов во время компиляции для создания одного класса, оба из одной сборки:

Файл 1:

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

Файл 2:

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

Частицы не используются широко, за исключением случаев, когда речь идет о сгенерированных файлах, таких как файлы Linq2Sql, EDM, WinForms и т. Д.

0 голосов
/ 06 января 2009

Зачем вам это нужно, чтобы быть частным? Защищенных должно быть достаточно, здесь. Вы просите автора подкласса написать код, который они не могут вызвать. Что это делает? Они могут использовать этот код в любом случае.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...