サブクラスのフィールドまたはプロパティをオーバーライドする


145

抽象基本クラスがあり、この親クラスから継承する各クラスで異なる値を持つフィールドまたはプロパティを宣言したい。

基本クラスのメソッドで参照できるように、それを基本クラスで定義したいと思います。たとえば、ToStringをオーバーライドして、「このオブジェクトはプロパティ/フィールドタイプです」と言っています。これを行う方法は3つありますが、疑問に思っていました。これを行うための最良の方法または受け入れられている方法は何ですか。初心者の質問、申し訳ありません。

オプション1:
抽象プロパティを使用し、継承したクラスでそれをオーバーライドします。これは強制されることでメリットがあり(オーバーライドする必要があります)、クリーンです。しかし、フィールドをカプセル化するのではなく、ハードコード値を返すのは少し間違っているように感じられ、単なるコード行ではなく、数行のコードです。また、「セット」の本体を宣言する必要がありますが、それはそれほど重要ではありません(そして、私が知らないものを回避する方法がおそらくあるでしょう)。

abstract class Father
{
    abstract public int MyInt { get; set;}
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
        set { }
    }
}

オプション2
パブリックフィールド(または保護フィールド)を宣言し、継承したクラスで明示的にオーバーライドできます。以下の例では、「new」を使用するように警告が表示され、おそらくそれを実行できますが、それは間違っていると感じ、多型性を壊します。良い考えではないようです...

abstract class Mother
{
    public int MyInt = 0;
}

class Daughter : Mother
{
    public int MyInt = 1;
}

オプション3
保護されたフィールドを使用して、コンストラクタで値を設定できます。これはかなり整頓されているように見えますが、コンストラクターが常にこれを設定し、複数のオーバーロードされたコンストラクターがあると、いくつかのコードパスが値を設定しない可能性が常にあることを保証する私に依存しています。

abstract class Aunt
{
    protected int MyInt;
}

class Niece : Aunt
{
    public Niece()
    {
        MyInt = 1;
    }
}

これは少し理論的な質問です。唯一の安全なオプションであるため、答えはオプション1である必要があると思いますが、私はC#に慣れつつあり、より経験のある人にこれを尋ねたかったのです。


abstract public int MyInt {get; set;} => public abstract string IntentName {get; セット;}:D
Navid Golforoushan '26 / 06/19

回答:


136

3つのソリューションのうち、オプション1のみがポリモーフィックです。

フィールド自体はオーバーライドできません。これが、オプション2新しいキーワードの警告を返す 理由です。

警告の解決策は、「新しい」キーワードを追加することではなく、オプション1を実装することです。

フィールドをポリモーフィックにする必要がある場合は、フィールドをプロパティにラップする必要があります。

オプション3ポリモーフィックな動作が必要ない場合は、で問題ありません。ただし、実行時にプロパティMyIntにアクセスすると、派生クラスは返される値を制御できないことに注意してください。基本クラス自体は、この値を返すことができます。

これは、プロパティの真にポリモーフィックな実装がどのように見えるかであり、派生クラスを制御できるようにます。

abstract class Parent
{
    abstract public int MyInt { get; }
}

class Father : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "X" and return a value */ }
    }
}

class Mother : Parent
{
    public override int MyInt
    {
        get { /* Apply formula "Y" and return a value */ }
    }
}

153
余談ですが、私はは「Y」、母は論理的には「X」という公式を適用すべきだと思います。
ピーター-モニカの復活2014年

4
Parentにデフォルトの実装を提供し、それを抽象化しない場合はどうなりますか?
アーロンフランケ

@AaronFranke署名を作成します。public virtual int MyInt {get; }
テッドビガム

@ Peter-
ReinstateMonica

18

オプション2はスターターではありません。フィールドを上書きすることはできません。非表示にすることしかできません。

個人的には、毎回オプション1を選びます。フィールドは常に非公開にしようとしています。もちろん、そのプロパティを本当にオーバーライドできるようにする必要がある場合です。別のオプションは、コンストラクターパラメーターから設定される基本クラスに読み取り専用プロパティを持つことです。

abstract class Mother
{
    private readonly int myInt;
    public int MyInt { get { return myInt; } }

    protected Mother(int myInt)
    {
        this.myInt = myInt;
    }
}

class Daughter : Mother
{
    public Daughter() : base(1)
    {
    }
}

インスタンスの存続期間にわたって値が変化しない場合は、おそらくこれが最も適切なアプローチです。


私たちはこれに基づいて、これは今正しくないと言うことができmsdn.microsoft.com/en-us/library/9fkccyh4.aspxプロパティをオーバーライドすることができ、MSDNの記事ショー
codingbiz

1
@codingbiz:プロパティに関する私の答えはどこにありますか?フィールドとプロパティは同じものではありません。
Jon Skeet 2014

@codingbiz:(確かに、私の答え確かにプロパティについて語っていますが、それらをオーバーライドできないとは決して言いませんでした。フィールドをオーバーライドすることはできないと言いました- まだ正しいです。)
Jon Skeet

7

オプション2は悪い考えです。シャドウイングと呼ばれるものになります。基本的に、2つの異なる "MyInt"メンバーがあり、1つは母親、もう1つは娘です。この問題は、母親に実装されているメソッドが母親の「MyInt」を参照するのに対し、娘に実装されているメソッドは娘の「MyInt」を参照することです。これはいくつかの深刻な読みやすさの問題を引き起こし、後で混乱を引き起こす可能性があります。

個人的には、最良の選択肢は3だと思います。それは明確な一元化された値を提供し、独自のフィールドを定義する手間をかけずに子によって内部的に参照できるためです。これはオプション1の問題です。


6

あなたはこれを行うことができます

class x
{
    private int _myInt;
    public virtual int myInt { get { return _myInt; } set { _myInt = value; } }
}

class y : x
{
    private int _myYInt;
    public override int myInt { get { return _myYInt; } set { _myYInt = value; } }
}

virtualを使用すると、何かを実行する本体のプロパティを取得し、サブクラスでそれをオーバーライドできます。


4

次のように定義できます。

abstract class Father
{
    //Do you need it public?
    protected readonly int MyInt;
}

class Son : Father
{
    public Son()
    {
        MyInt = 1;
    }
}

値を読み取り専用に設定することにより、オブジェクトの存続期間中、そのクラスの値が変更されないままになります。

次の質問だと思います:なぜそれが必要なのですか?


静的は、値がクラスのすべてのインスタンス間で共有されることを意味するため、単語の選択としては適していません。もちろん、そうではありません。
Winston Smith、

3

クラスを構築していて、プロパティのベース値が必要な場合virtualは、ベースクラスでキーワードを使用します。これにより、オプションでプロパティをオーバーライドできます。

上記の例を使用:

//you may want to also use interfaces.
interface IFather
{
    int MyInt { get; set; }
}


public class Father : IFather
{
    //defaulting the value of this property to 1
    private int myInt = 1;

    public virtual int MyInt
    {
        get { return myInt; }
        set { myInt = value; }
    }
}

public class Son : Father
{
    public override int MyInt
    {
        get {

            //demonstrating that you can access base.properties
            //this will return 1 from the base class
            int baseInt = base.MyInt;

            //add 1 and return new value
            return baseInt + 1;
        }
        set
        {
            //sets the value of the property
            base.MyInt = value;
        }
    }
}

プログラム内:

Son son = new Son();
//son.MyInt will equal 2

0

私はオプション3で行きますが、サブクラスが強制的に実装される抽象setMyIntメソッドがあります。これにより、派生クラスがコンストラクターに設定し忘れるという問題が発生しなくなります。

abstract class Base 
{
 protected int myInt;
 protected abstract void setMyInt();
}

class Derived : Base 
{
 override protected void setMyInt()
 {
   myInt = 3;
 }
}

ちなみに、オプション1では、セットを指定しないと、抽象基本クラスのプロパティでは、派生クラスはそれを実装する必要はありません。

abstract class Father
{
    abstract public int MyInt { get; }
}

class Son : Father
{
    public override int MyInt
    {
        get { return 1; }
    }
}

0

コンストラクターでプロパティ値を要求するように抽象基本クラスを変更する場合は、オプション3を使用できます。パスを見逃すことはありません。私は本当にこのオプションを検討します。

abstract class Aunt
{
    protected int MyInt;
    protected Aunt(int myInt)
    {
        MyInt = myInt;
    }

}

もちろん、フィールドをプライベートにして、必要に応じて、保護されたプロパティまたはパブリックプロパティのゲッターを公開することもできます。


0

これは私がしました...

namespace Core.Text.Menus
{
    public abstract class AbstractBaseClass
    {
        public string SELECT_MODEL;
        public string BROWSE_RECORDS;
        public string SETUP;
    }
}

namespace Core.Text.Menus
{
    public class English : AbstractBaseClass
    {
        public English()
        {
            base.SELECT_MODEL = "Select Model";
            base.BROWSE_RECORDS = "Browse Measurements";
            base.SETUP = "Setup Instrument";
        }
    }
}

この方法でもフィールドを使用できます。


これは、プロトタイピングやデモのアドホックソリューションとしては素晴らしいと思います。
Zimano

0

実装を持つ抽象クラスが必要な場合の実装例。サブクラスは:

  1. 抽象クラスの実装をパラメーター化します。
  2. 抽象クラスの実装を完全に継承します。
  3. 独自の実装があります。

この場合、実装に必要なプロパティは、抽象クラスとその独自のサブクラスを除いて使用できません。

    internal abstract class AbstractClass
    {
        //Properties for parameterization from concrete class
        protected abstract string Param1 { get; }
        protected abstract string Param2 { get; }

        //Internal fields need for manage state of object
        private string var1;
        private string var2;

        internal AbstractClass(string _var1, string _var2)
        {
            this.var1 = _var1;
            this.var2 = _var2;
        }

        internal void CalcResult()
        {
            //The result calculation uses Param1, Param2, var1, var2;
        }
    }

    internal class ConcreteClassFirst : AbstractClass
    {
        private string param1;
        private string param2;
        protected override string Param1 { get { return param1; } }
        protected override string Param2 { get { return param2; } }

        public ConcreteClassFirst(string _var1, string _var2) : base(_var1, _var2) { }

        internal void CalcParams()
        {
            //The calculation param1 and param2
        }
    }

    internal class ConcreteClassSecond : AbstractClass
    {
        private string param1;
        private string param2;

        protected override string Param1 { get { return param1; } }

        protected override string Param2 { get { return param2; } }

        public ConcreteClassSecond(string _var1, string _var2) : base(_var1, _var2) { }

        internal void CalcParams()
        {
            //The calculation param1 and param2
        }
    }

    static void Main(string[] args)
    {
        string var1_1 = "val1_1";
        string var1_2 = "val1_2";

        ConcreteClassFirst concreteClassFirst = new ConcreteClassFirst(var1_1, var1_2);
        concreteClassFirst.CalcParams();
        concreteClassFirst.CalcResult();

        string var2_1 = "val2_1";
        string var2_2 = "val2_2";

        ConcreteClassSecond concreteClassSecond = new ConcreteClassSecond(var2_1, var2_2);
        concreteClassSecond.CalcParams();
        concreteClassSecond.CalcResult();

        //Param1 and Param2 are not visible in main method
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.