web-dev-qa-db-ja.com

継承された読み取り専用フィールドに値を割り当てますか?

だから私はたくさんの子供たちがいる基本クラスを持っています。この基本クラスは、デフォルト値を持ついくつかの読み取り専用プロパティと変数を定義します。これらは、子供によって異なる場合があります。

読み取り専用のプロパティ/フィールドを使用すると、コンストラクター内の変数の値と定義を変更できますが、それ以外の場所では変更できません。子クラスのコンストラクターで継承された読み取り専用変数の値を変更しようとすると、「読み取り専用変数はコンストラクターでのみ割り当てることができます」というエラーが発生します。これはなぜですか。また、Reflectionを使用せずにこれを回避するにはどうすればよいですか。

私の意図:特定のフィールドを1回しか変更できないスクリプトを通じてユーザーの拡張性を許可すること。

25
Steffan Donal

アダムは正しい答えを持っています。占有するスペース(コンストラクター内のパラメーターの数?)が心配な場合は、別のソリューションで別の問題として対処する必要があります。これらすべてのプロパティを含むBaseConfigクラスを作成し、それだけです。 Baseは、BaseConfigのプロパティからすべての読み取り専用フィールドを割り当てるか、代わりにBaseConfigタイプの読み取り専用フィールドを1つだけ保持して、値を参照することができます。

これがなぜであるかについては、各クラスの読み取り専用フィールドがいつ初期化/初期化可能になるかについて、 C#コンストラクターの実行順序 を参照してください。

8
Tim Erickson

その理由は、そのクラスのコンストラクターのreadonlyフィールドにのみ割り当てることができるためです。
C#リファレンスのreadonlyの定義 (私の強調)によると:

フィールド宣言に読み取り専用修飾子が含まれている場合、宣言によって導入されたフィールドへの割り当ては、宣言の一部として、または同じクラスのコンストラクターでのみ発生します。 )。

これを回避するには、読み取り専用プロパティのパラメーターを受け取る保護されたコンストラクターをベースに作成します。

例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Base b = new Child();
            Console.WriteLine(b.i);
            Console.Read();
        }
    }

    class Base
    {
        public readonly int i;

        public Base()
        {
            i = 42;
        }

        protected Base(int newI)
        {
            i = newI;
        }
    }

    class Child : Base
    {
        public Child()
            : base(43)
        {}
    }
}
27

仮想getonlyプロパティを使用することで、探している正確な動作を取得できます。

public class BSE
{
    virtual public int Prop 
    {
        get
        {
            return 6;
         }
     }
}
public class Derived : BSE
{
    public override int Prop
    {
         get
         {
             return 10;
         }
    }
 }

フィールドは継承およびオーバーロードモデルの外側にあるため、ポリモーフィック機能を提供するために使用しないでください。

8
rerun

Public getaccessorおよびprotectedsetaccessorでプロパティを使用できます。派生クラスは、このプロパティの値を設定できます。

例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Base b = new Child();
            Console.WriteLine(b.I);
            Console.Read();
        }
    }

    class Base
    {
        public int I { get; protected set; }

        public Base()
        {
            I = 42;
        }
    }

    class Child : Base
    {
        public Child()
        {
            I = 43;
        }
    }
}
3
Imran Khan

これは設計上不可能です。値をprotected基本クラスコンストラクターに渡してみてください

0
Mikant