web-dev-qa-db-ja.com

C#コンストラクターチェーン? (どうやってするの?)

これはおそらく非常に単純な質問であることを知っていますが、私はしばらくの間この概念に苦労してきました。私の質問は、C#でコンストラクターをどのように連鎖させるかです。私は最初のOOPクラスにいるので、ただ学習しています。コンストラクターチェーンがどのように機能するのか、どのように実装するのか、また、チェーンなしでコンストラクターを実行するよりも優れている理由すらわかりません。

説明付きのいくつかの例に感謝します。

では、どのようにそれらを連鎖させるのでしょうか?私はそれが行く2つで知っています:

public SomeClass this: {0}

public SomeClass
{
    someVariable = 0
} 

しかし、3、4などでどのように行うのでしょうか?

繰り返しますが、これは初心者の質問であることを知っていますが、これを理解するのに苦労しており、理由はわかりません。

202
Alex

標準の構文(メソッドのようにthisを使用)を使用して、オーバーロードinsideクラスを選択します。

class Foo {
    private int id;
    private string name;
    public Foo() : this(0, "") {
    }
    public Foo(int id, string name) {
        this.id = id;
        this.name = name;
    }
    public Foo(int id) : this(id, "") {
    }
    public Foo(string name) : this(0, name) {
    }
}

その後:

Foo a = new Foo(), b = new Foo(456,"def"), c = new Foo(123), d = new Foo("abc");

以下にも注意してください:

  • base(...)を使用してベースタイプのコンストラクターにチェーンできます
  • 各コンストラクターに追加のコードを入れることができます
  • デフォルト(何も指定しない場合)はbase()です

「理由」:

  • コード削減(常に良いこと)
  • necessaryデフォルト以外のベースコンストラクタを呼び出すには、たとえば:

    SomeBaseType(int id) : base(id) {...}
    

ただし、同様の方法でオブジェクト初期化子を使用することもできます(何も記述する必要はありません)。

SomeType x = new SomeType(), y = new SomeType { Key = "abc" },
         z = new SomeType { DoB = DateTime.Today };
309
Marc Gravell

これを探している人に有効なポイントを提示したいだけです。 4.0(VS2010)より前の.NETバージョンを使用する場合は、上記のようにコンストラクターチェーンを作成する必要があることに注意してください。

ただし、4.0にとどまっている場合、良いニュースがあります。オプションの引数を持つ単一のコンストラクタを持つことができます! Fooクラスの例を単純化します。

class Foo {
  private int id;
  private string name;

  public Foo(int id = 0, string name = "") {
    this.id = id;
    this.name = name;
  }
}

class Main() {
  // Foo Int:
  Foo myFooOne = new Foo(12);
  // Foo String:
  Foo myFooTwo = new Foo(name:"Timothy");
  // Foo Both:
  Foo myFooThree = new Foo(13, name:"Monkey");
}

コンストラクターを実装する場合、デフォルトが設定されているため、オプションの引数を使用できます。

このレッスンを楽しんだことを願っています! 2004/2005以降、開発者が構造の連鎖について不平を言っており、デフォルトのオプション引数を使用できないことを信じられません。現在、開発の世界ではSO時間がかかりました。開発者は下位互換性がないため、使用することを恐れています。

56
ES3178

これは例を挙げて説明するのが最適です。イメージングにはクラスPersonがあります

public Person(string name) : this(name, string.Empty)
{
}

public Person(string name, string address) : this(name, address, string.Empty)
{
}

public Person(string name, string address, string postcode)
{
    this.Name = name;
    this.Address = address;
    this.Postcode = postcode;
}

そのため、ここにはいくつかのプロパティを設定し、コンストラクターチェーンを使用して、名前だけ、または名前とアドレスだけでオブジェクトを作成できるコンストラクターがあります。名前だけでインスタンスを作成すると、名前とアドレスにデフォルト値string.Emptyが送信され、Postcodeのデフォルト値が最終コンストラクターに送信されます。

そうすることで、あなたが書いたコードの量を減らしています。実際には1つのコンストラクターのみにコードが含まれているため、繰り返しはしていません。たとえば、Nameをプロパティから内部フィールドに変更する場合は、1つのコンストラクターを変更するだけで済みます。それを変更するための3つの場所になります。

28
blowdart

私は日記クラスを持っているので、何度も値を設定することを書いていません

public Diary() {
    this.Like = defaultLike;
    this.Dislike = defaultDislike;
}

public Diary(string title, string diary): this()
{
    this.Title = title;
    this.DiaryText = diary;
}

public Diary(string title, string diary, string category): this(title, diary) {
    this.Category = category;
}

public Diary(int id, string title, string diary, string category)
    : this(title, diary, category)
{
    this.DiaryID = id;
}
10
KhanSharp

「Constructor Chain」の使用法は何ですか?
別のコンストラクターから1つのコンストラクターを呼び出すために使用します。

「コンストラクタチェーン」を実装するにはどうすればよいですか?
コンストラクターの定義後に「:this(yourProperties)」キーワードを使用します。例えば:

Class MyBillClass
{
    private DateTime requestDate;
    private int requestCount;

    public MyBillClass()
    {
        /// ===== we naming "a" constructor ===== ///
        requestDate = DateTime.Now;
    }
    public MyBillClass(int inputCount) : this()
    {
        /// ===== we naming "b" constructor ===== ///
        /// ===== This method is "Chained Method" ===== ///
        this.requestCount= inputCount;
    }
}

なぜ便利なのですか?
重要な理由は、コーディングの削減とコードの重複防止です。プロパティを初期化するための繰り返しコードなどクラスの一部のプロパティを特定の値(サンプルではrequestDate)で初期化する必要があるとします。また、クラスには2つ以上のコンストラクタがあります。 「コンストラクターチェーン」がないと、クラスのすべてのコンストラクターで初期化コードを繰り返す必要があります。

どのように機能しますか?(または、「コンストラクターチェーン」の実行シーケンスとは)?
上記の例では、最初にメソッド「a」が実行され、次に命令シーケンスがメソッド「b」に戻ります。言い換えれば、上記のコードは以下と同等です。

Class MyBillClass
{
    private DateTime requestDate;
    private int requestCount;

    public MyBillClass()
    {
        /// ===== we naming "a" constructor ===== ///
        requestDate = DateTime.Now;
    }
    public MyBillClass(int inputCount) : this()
    {
        /// ===== we naming "b" constructor ===== ///
        // ===== This method is "Chained Method" ===== ///

        /// *** --- > Compiler execute "MyBillClass()" first, And then continue instruction sequence from here
        this.requestCount= inputCount;
    }
}
7
M.i.T

これについて尋ねていますか?

  public class VariantDate {
    public int day;
    public int month;
    public int year;

    public VariantDate(int day) : this(day, 1) {}

    public VariantDate(int day, int month) : this(day, month,1900){}

    public VariantDate(int day, int month, int year){
    this.day=day;
    this.month=month;
    this.year=year;
    }

}
6

次の例が、コンストラクターチェーンに光を当てることを願っています。
ここでの使用例では、ユーザーがコンストラクターにディレクトリを渡すことを期待していますが、ユーザーはどのディレクトリを渡すかわからないため、デフォルトのディレクトリを割り当てることにします。ステップアップして、動作すると思われるデフォルトのディレクトリを割り当てます。

ところで、*。Dump()が何なのか疑問に思う場合に備えて、この例ではLINQPadを使用しました。
歓声

void Main()
{

    CtorChaining ctorNoparam = new CtorChaining();
    ctorNoparam.Dump();
    //Result --> BaseDir C:\Program Files (x86)\Default\ 

    CtorChaining ctorOneparam = new CtorChaining("c:\\customDir");
    ctorOneparam.Dump();    
    //Result --> BaseDir c:\customDir 
}

public class CtorChaining
{
    public string BaseDir;
    public static string DefaultDir = @"C:\Program Files (x86)\Default\";


    public CtorChaining(): this(null) {}

    public CtorChaining(string baseDir): this(baseDir, DefaultDir){}

    public CtorChaining(string baseDir, string defaultDir)
    {
        //if baseDir == null, this.BaseDir = @"C:\Program Files (x86)\Default\"
        this.BaseDir = baseDir ?? defaultDir;
    }
}
2
yantaq

コンストラクターチェーンにはもう1つの重要なポイントがあります。順序です。どうして?デフォルトのコンストラクターを期待するフレームワークによって実行時に構築されるオブジェクトがあるとしましょう。必要なときにコンストラクター引数を渡す機能を持ちながら、値を渡すことができるようにしたい場合、これは非常に便利です。

たとえば、デフォルトのコンストラクターによってデフォルト値に設定されるバッキング変数を持つことができますが、上書きすることができます。

public class MyClass
{
  private IDependency _myDependency;
  MyClass(){ _myDependency = new DefaultDependency(); }
  MYClass(IMyDependency dependency) : this() {
    _myDependency = dependency; //now our dependency object replaces the defaultDependency
  }
}
1
arviman