web-dev-qa-db-ja.com

抽象基本クラスを持たずに、子孫のメソッドを強制的にオーバーライドするにはどうすればよいですか?

質問の見出しは少し混乱しているようですが、ここで質問をクリアしてみます

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

    }
}

コード全体を理解しようとしないでください。私はそれを要約しています。

  1. EmployeeはAbstractクラスであり、AbstractメソッドGiveBonusがあります
  2. SalesPersonはEmployeeから派生しています。 SalesPersonは、抽象メソッドGiveBonusに定義を与える必要があります。(SalesPersonは抽象にできません
  3. PTSalesPersonはSalesPersonから派生しています。

私の質問は、どうすればforcePTSalesPersonに独自のGiveBonus実装を持たせることができますか

42
Vaibhav Jain

あなたはこれについて間違った方法で考えていると思います。言語設計者は、「私たちが本当に必要なのは、メソッドをオーバーライドする必要があるとしてマークする方法です。abstract "と呼ばれるものを発明しましょう。彼らは、「仮想メソッドは、この基本型のすべての派生型がこのメソッドを実行できるはずです。という考えを表すことができます。しかし、賢明なコードなしメソッドの基本クラスバージョンに入れることができますか?その状況の抽象メソッドと呼ばれるものを発明しましょう。」

それが抽象メソッドの解決を意図した問題です。すべての派生クラスに共通のメソッドがありますが、実用的な基本クラスの実装はありません。「派生型に実装を強制する方法が必要」ではありません。派生型は、実装を提供することを強制されます-結果結果ですが、そもそも解決することを意図した問題ではありません。

C#言語には、「サブメソッドにこのメソッドの独自の実装を提供するよう強制する必要がある」という問題に対するメカニズムがありません。お客様。

あなたへの私の質問は:なぜあなたはこれをしたいのですか?基本クラスの実装が派生クラスにとって正しいかどうかを判断するのは、派生クラスの開発者次第です。それはあなた次第ではありません。そして、あなたがそれをする何らかの方法を持っていたとしても、開発者が単に言うことを止めるもの

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

派生クラスの開発者にこの作業を強制しようとする目的は何ですか?おそらくあなたが望むものを達成するためのより良い方法があります。

しかしより一般的には、あなたの階層がそもそも賢明に設計されているかどうかはわかりません。従業員にメソッドGiveBonusが表示されるとき、これは「従業員がボーナスを受け取ることができる」ではなく、「従業員がボーナスを与えることができる」ことを意味すると想定します。確かにマネージャーはボーナスを与え、従業員受信はボーナスを与えます。従業員の階層をやりすぎているのではないかと思います。

93
Eric Lippert

SalesPersonを抽象化するか、階層を変更しない限り、できません。

どうですか:

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

EmployeeとSalesPersonBaseの両方が抽象としてマークされるようになりました。

ただし、PTSalesPersonに動作を継承するだけでなく、is-a関係も継承する必要がある場合(PTSalesPersonはSalesPersonでもあります)、これを強制する方法はありません。

上記のテキストは、コンパイル時のチェックのみを考慮する場合にのみ有効です。言い換えると、PTSalesPersonクラスにオーバーライドを追加していない場合にコンパイラーに文句を言わせたい場合、上記で説明したことをしない限り、それを行うことはできません。

ただし、実行時にメソッドを検査するためにリフレクションを使用し、PTSalesPersonのメソッドが明示的にオーバーライドされていない場合は例外をスローすることを妨げるものはありませんが、ハックと見なします。

依存性注入を使用します。 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のインスタンスを明示的に提供する必要があります(またはNullReferenceExceptionメソッドでGiveBonusを取得します)。

追加のボーナスとして、このアプローチにより、Personのさまざまなサブクラスが適切な場合にボーナス計算方法を共有できます。

編集

もちろん、PTSalesPersonSalesPersonから派生し、そのコンストラクターが基本コンストラクターを呼び出す場合、これも機能しません。

7
Robert Rossney

クラスEmployeeを抽象として宣言しますが、"Must be implement by subclasses"のようなメッセージでランタイム例外をスローするGiveBonus()を提供および実装します。古いSmalltalkの慣習です... C#にとって有用かどうかはわかりません。 Javaコードで使用しました。

6
JuanZe

WAは「インターフェース」を使用している可能性があるため、IBonusGiver {void GiveBonus(); }次に、抽象メソッドとオーバーライドの代わりに、この新しいインターフェイスをすべてのクラスに実装します。例えばPTSalesPerson:SalesPerson、IBonusGiverは、各クラスで新しい実装を強制します。

5
mind_overflow

これは非常に古いスレッドですが、 この質問 で与えられた答えが役立つ場合があります。派生クラスに、仮想メソッド/プロパティの独自の実装を強制することができます。

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

仮想メソッドが抽象として宣言されている場合、抽象クラスから継承するすべてのクラスに対して仮想メソッドのままです。抽象メソッドを継承するクラスは、メソッドの元の実装にアクセスできません。前の例では、クラスFのDoWorkはクラスDのDoWorkを呼び出すことができません。このように、抽象クラスは派生クラスに仮想メソッドの新しいメソッド実装を強制的に提供できます。

5
Vincent

これは、実装に実際に2つの部分があることを示しているように感じます。基本クラスに含まれる部分実装と、サブクラスに必要な実装の完了が欠落している部分です。例:

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

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

初期実装が不完全であるため、オーバーライドを強制する必要があります。できることは、実装を2つの部分に分割することです。実装された部分部分と、実装待ちの欠落部分です。例:

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
   }
}
2
Dave Cousineau

SalesPerson Abstractを作成できない場合、この作業を行う唯一の方法は次のとおりです。

1)SalesPerson.GiveBonus(...)でリフレクションを使用して、「this」がSalesPerson、または派生クラスであるかどうかを判断しますa)派生クラスではない場合、SalesPerson.GiveBonusで現在のコードを実行します(これを仮想として宣言し、SalesPersonの実装が例外をスローするようにします。)

ここでの欠点は、反射が遅いことです。 GiveBonusDerivedが宣言されていない場合など、コンパイル時エラーはありません。

1
FallenAvatar

説明したセットアップを使用することはできません。 PTSalesPersonは、SalesPersonから継承するため、GiveBonusの実装を既に持っています。

1
hackerhasid

SalesPersonインスタンスを直接作成しない場合、最も簡単なことは、SalesPersonを抽象化することです。これにより、子クラスが代わりにそれを実装するよう強制されます(または抽象化されます)。

そのメソッドにすべてのSalesPeopleに共通のロジックがある場合、SalesPersonにGiveBonusを テンプレートメソッド として実装できます。サブクラスで必要な抽象メソッドを呼び出します:

どちらの方法でも、子クラスに実装を強制する唯一の方法は、基本クラスを抽象化することです。

1
Lee

SalesPersonの実装で常にNotImplementedExceptionがスローされるようにすることができます。 :Vしかし、契約上、いいえ、それはできません。

0
Stephen

これはかなり古いですが、やや似たような状況があります。基本クラスは構成ファイルをロードし、基本クラスのデフォルトを設定します。ただし、構成ファイルには、継承されたクラスのデフォルト値を含めることもできます。

これが、複合機能を取得する方法です。

基本クラスのメソッド

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

さて、この投稿が古いことは知っていますが、最近この回答を調べる必要がありましたので、これを探している他の人のために:(私は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"
0
Arkaine80