Подтвердить что ты не робот

Как заставить переопределить метод у потомка, не имея абстрактного базового класса?

Заголовок вопроса кажется немного запутанным, но я постараюсь разобраться с этим вопросом.

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

    }
}

Пожалуйста, не пытайтесь понять весь код. Я обобщаю его.

  • Employee - это абстрактный класс, который имеет абстрактный метод GiveBonus
  • SalesPerson - это результат Employee. SalesPerson должен дать определение абстрактному методу GiveBonus. (SalesPerson не может быть абстрактным)
  • PTSalesPerson происходит от SalesPerson.

Теперь мой вопрос: как я могу заставить PTSalesPerson иметь собственную реализацию GiveBonus.

4b9b3361

Ответ 1

Я думаю, вы думаете об этом неправильно. Дизайнеры языка не сказали себе: "Нам действительно нужен способ отметить метод, который должен быть переопределен, пусть придумают эту вещь как абстрактный". Они сказали: "Виртуальный метод позволяет нам представить идею о том, что каждый производный тип этого базового типа должен иметь возможность делать этот метод. Но что, если нет разумного кода, который может идти в версии базового класса метода?, придумать эту вещь, названную абстрактным методом для этого обстоятельства".

Проблема, с которой были решены абстрактные методы: у вас есть метод, общий для всех производных классов, но без разумной реализации базового класса, НЕ "Мне нужен способ заставить мои производные типы обеспечить реализацию". Эти производные типы вынуждены обеспечивать реализацию, является следствием решения, но не проблема, которая должна быть решена в первую очередь.

Язык С# не имеет механизма для проблемы "Я должен заставить свой подтип предоставить свою собственную реализацию этого метода", потому что это не проблема, которую разработчики языка, насколько мне известно, когда-либо рассматривали, были бы проблемой для большинство наших клиентов.

Итак, мой вопрос к вам: почему вы хотите это сделать? Разумеется, разработчик производного класса должен определить, правильна ли реализация базового класса для производного класса или нет. Это не вам. И даже если бы у вас был какой-то способ сделать это, что бы остановило разработчика просто сказать

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

?

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

Но в целом: я не уверен, что ваша иерархия разумно разработана в первую очередь. Когда я вижу метод GiveBonus на Работника, я предполагаю, что это означает, что "работник может дать бонус", а не "работник может получить бонус". Разумеется, менеджер дает бонус, а работник получает бонус. Я думаю, что вы можете сделать иерархию сотрудников слишком много.

Ответ 2

Вы не можете, если вы не сделаете SalesPerson абстрактным или не измените иерархию.

Как насчет:

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

Оба Employee и SalesPersonBase теперь помечены как абстрактные.

Однако, если вам требуется, чтобы PTSalesPerson не только наследовал поведение, но и наследовал отношение is-a (PTSalesPerson также является SalesPerson), тогда вы не можете заставить это.

Примечание. Вышеприведенный текст действителен только в том случае, если вы рассматриваете только проверки времени компиляции. Другими словами, если вы хотите, чтобы компилятор жаловался, если вы не добавили переопределение в класс PTSalesPerson, вы не сможете этого сделать, если вы не сделаете то, что я изложил выше.

Однако нет ничего, что помешало бы вам использовать рефлексию для изучения методов во время выполнения и выдавать исключение, если метод в PTSalesPerson явно не переопределен, однако я бы подумал, что взлом.

Ответ 3

Используйте инъекцию зависимостей. Создайте класс BonusCalculator:

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

В базовом классе:

private BonusCalculator Calculator { get; set; }

public void GiveBonus()
{
   Bonus = Calculator.CalculateBonus(this)
}

В вашем конструкторе реализации:

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

Кому-то, выполняющему подкласс Person, теперь необходимо явно предоставить ему экземпляр BonusCalculator (или получить a NullReferenceException в методе GiveBonus).

Как добавленный, er, бонус, этот подход позволяет различным подклассам Person делиться методом бонусных вычислений, если это необходимо.

Edit

Конечно, если PTSalesPerson происходит от SalesPerson, и его конструктор вызывает базовый конструктор, это тоже не сработает.

Ответ 4

Объявить класс Employee абстрактным, но предоставить и реализовать GiveBonus(), который выдает исключение среды выполнения с сообщением типа "Должен быть реализован подклассами". Это старая практика Smalltalk... Не уверен, что это полезно для С#. Я использовал его в Java-коде.

Ответ 5

WA может использовать "интерфейс", поэтому вы определяете его как IBonusGiver {void GiveBonus(); } Затем вместо абстрактного метода и переопределений вы реализуете во всех своих классах этот новый интерфейс. например PTSalesPerson: SalesPerson, IBonusGiver заставляет новую реализацию в каждом классе.

Ответ 6

Вы не можете использовать описанную вами настройку. PTSalesPerson уже будет иметь реализацию GiveBonus, поскольку он наследует от SalesPerson.

Ответ 7

Если вы не собираетесь создавать экземпляр SalesPerson напрямую, то проще всего сделать это, чтобы сделать реферат SalesPerson, и это заставит все дочерние классы реализовать его (или быть абстрактным сами).

Если в этом методе есть общий алгоритм, аналогичный всем SalesPeople, то вы можете реализовать GiveBonus на SalesPerson как метод шаблона. Какой вызов требует абстрактного метода в любых подклассах:

Какой бы способ вы ни выбрали, единственный способ принудительно реализовать реализацию в дочернем классе - сделать абстрактное базовое класс.

Ответ 8

Единственный способ увидеть эту работу, если вы не можете сделать SalesPerson Abstract, заключается в следующем:

1) в SalesPerson.GiveBonus(...) использовать отражение, чтобы определить, является ли 'this' SalesPerson или производным классом a) если не производный класс, выполните текущий код в SalesPerson.GiveBonus б) иначе вызовите GiveBonusDerived. (объявите это как виртуальное и сделайте импликацию в SalesPerson выкинуть исключение.)

Здесь отступают спины, Reflection - медленный. Нет ошибки компиляции, если GiveBonusDerived не объявлен и т.д.

Ответ 9

Вы всегда можете заставить реализацию SalesPerson выбрасывать NotImplementedException.: V Но контрактно, нет, вы не можете этого сделать.

Ответ 10

Я чувствую, что это означает, что ваша реализация действительно состоит из двух частей: частичная реализация, которая идет в базовом классе, и недостающее завершение этой реализации, которое вы хотите в подклассе. Пример:

public abstract class Base
{
   public virtual void PartialImplementation()
   {
      // partial implementation
   }
}

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

Вы хотите принудительно переопределить, так как первоначальная реализация является неполной. Что вы можете сделать, так это разделить вашу реализацию на две части: реализованную частичную часть и отсутствующую часть ожидающей реализации. Пример:

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

Ответ 11

Это довольно старый, но у нас есть несколько схожая ситуация. Базовый класс загружает файл конфигурации и устанавливает значения по умолчанию для базового класса. Однако файл конфигурации также может содержать значения по умолчанию для унаследованных классов.

Вот как мы получили объединенную функциональность.

Методы базового класса

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);

Ответ 12

Хорошо, я знаю, что этот пост старый, однако мне недавно нужно было посмотреть этот ответ, так что для кого-то еще ищущего это: (Я использую VS 2012.net 4.5, поэтому не уверен в более старых версиях)

Я создал абстрактный класс и использовал переопределение на обоих дочерних классах:

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

Тестирование:

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"

Ответ 13

Это очень старый поток, но ответ на этот вопрос может быть полезен. Вы можете заставить производный класс иметь собственную реализацию виртуального метода/свойства.

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

Если виртуальный метод объявлен абстрактным, он по-прежнему является виртуальным для любого класс, наследующий от абстрактного класса. Класс, наследующий абстрактный метод не может получить доступ к первоначальной реализации метод - в предыдущем примере DoWork в классе F не может вызвать DoWork на классе D. Таким образом, абстрактный класс может заставить производные классы для предоставления новых методов для виртуальных методов.

Ответ 14

Я очень надеюсь, что мой ответ поможет некоторым людям смутить эту проблему.

Потерпите меня, но я попытаюсь обобщить то, что задают, просто чтобы убедиться, что я отвечаю на правильный вопрос. Тогда я дам ответ!

Я думаю, что суть вопроса: как объявить метод в абстрактном классе, так что он имеет реализацию, но все же требует, чтобы производный класс переопределял метод?

С#, похоже, не поддерживает это. Если вы объявите метод "абстрактным", ему не разрешено иметь реализацию (тело). Но если вы объявите его "виртуальным", производный класс не будет вынужден переопределять его. С# deosn't позволяет методу быть отмеченным как абстрактным, так и виртуальным.

Я считаю, что есть много ситуаций, когда вы хотите сделать это (что задается).

Способом решения этой загадки является следующее: Объявите два метода! Один из них отмечен "абстрактным"; другой отмечен как "виртуальный" и вызывает первый в своем теле.

Таким образом, производный класс вынужден переопределить абстрактный метод, но (частичную) реализацию в виртуальном методе можно безопасно унаследовать.

Итак, например, метод Employee GiveBonus может быть объявлен таким образом:

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

Я оставляю детали PostBonus для воображения, но красота этого подхода заключается в том, что производные классы вынуждены переопределять ComputeBonus, но преимущества GiveBonus от частичной реализации, предоставляемые в базовом классе. НТН