web-dev-qa-db-ja.com

C#:コンストラクターとインスタンス化を介してプロパティにデータを割り当てる

Albumクラスがあるとします:

public class Album 
{
    public string Name {get; set;}
    public string Artist {get; set;}
    public int Year {get; set;}

    public Album()
    { }

    public Album(string name, string artist, int year)
    {
        this.Name = name;
        this.Artist = artist;
        this.Year = year;
    }
}

タイプAlbumのオブジェクトにデータを割り当てたい場合、次の2つのアプローチの違いは何ですか:

コンストラクター経由

var albumData = new Album("Albumius", "Artistus", 2013);

または インスタンス化するとき

var albumData = new Album 
                    {
                         Name = "Albumius",
                         Artist = "Artistus",
                         Year = 2013
                    };
84
VasileF

どちらのアプローチもコンストラクターを呼び出しますが、単に異なるコンストラクターを呼び出します。このコード:

var albumData = new Album 
                {
                     Name = "Albumius",
                     Artist = "Artistus",
                     Year = 2013
                };

この同等のコードの構文略記です:

var albumData = new Album();
albumData.Name = "Albumius";
albumData.Artist = "Artistus";
albumData.Year = 2013;

2つは、コンパイル後のidenticalです。したがって、パラメーターなしのコンストラクターがパブリックでない場合:

public Album() { }

とにかくオブジェクト初期化子を使用することはできません。 したがって、主な質問は、オブジェクトを初期化するときに使用するものではなく、オブジェクトが最初に公開するコンストラクタですオブジェクトが2つのコンストラクタを公開する場合(例のように) 、その後、オブジェクトを構築するために両方の方法が等しく有効であると仮定できます。

オブジェクトは、構築のために特定の値requireを必要とするため、パラメーターなしのコンストラクターを公開しない場合があります。ただし、そのような場合でも、他の値に初期化子構文を使用できます。たとえば、オブジェクトに次のコンストラクタがあるとします。

private Album() { }
public Album(string name)
{
    this.Name = name;
}

パラメーターなしのコンストラクターはプライベートであるため、使用できません。ただし、もう一方を使用して、初期化子構文を引き続き使用できます。

var albumData = new Album("Albumius")
                {
                     Artist = "Artistus",
                     Year = 2013
                };

コンパイル後の結果は次のようになります:

var albumData = new Album("Albumius");
albumData.Artist = "Artistus";
albumData.Year = 2013;
110
David

オブジェクト初期化子は、クラスをインラインでセットアップできるので便利です。トレードオフは、クラスを不変にできないことです。考慮してください:

public class Album 
{
    // Note that we make the setter 'private'
    public string Name { get; private set; }
    public string Artist { get; private set; }
    public int Year { get; private set; }

    public Album(string name, string artist, int year)
    {
        this.Name = name;
        this.Artist = artist;
        this.Year = year;
    }
}

クラスがこのように定義されている場合、クラスの構築後にクラスの内容を変更する簡単な方法は実際にはないことを意味します。不変性には利点があります。何かが不変である場合、それが正しいと判断するのが簡単ですMUCHです。結局のところ、構築後に変更できない場合は、(構造が正しいと判断した後に)「間違った」状態になることはありません。次のような匿名クラスを作成する場合:

new { 
    Name = "Some Name",
    Artist = "Some Artist",
    Year = 1994
};

コンパイラーは不変クラスを自動的に作成します(つまり、構築後に匿名クラスを変更することはできません)。これは、不変性がまさに有用だからです。ほとんどのC++/Javaスタイルガイドは、多くの場合、この理由だけでconst(C++)またはfinal(Java)をメンバーにすることを推奨しています。可動部品が少ない場合、より大きなアプリケーションを検証する方がはるかに簡単です。

すべてを言って、クラスの構造をすばやく変更できるようにしたい場合があります。私がセットアップしたいツールがあるとしましょう:

public void Configure(ConfigurationSetup setup);

そして、私は次のような多くのメンバーを持つクラスを持っています:

class ConfigurationSetup {
    public String Name { get; set; }
    public String Location { get; set; }
    public Int32 Size { get; set; }
    public DateTime Time { get; set; }

    // ... and some other configuration stuff... 
}

オブジェクト初期化構文を使用すると、プロパティの一部の組み合わせを構成したい場合に便利ですが、必ずしも一度にすべてのプロパティが必要というわけではありません。たとえば、NameLocationを設定するだけの場合、次のようにします。

ConfigurationSetup setup = new ConfigurationSetup {
    Name = "Some Name",
    Location = "San Jose"
};

これにより、everyおそらく置換のための新しいコンストラクターを定義することなくいくつかの組み合わせを設定できます。

概して、クラスを不変にすると長期的には開発時間を大幅に節約できると主張しますが、オブジェクト初期化構文を使用すると、特定の構成順列の設定がはるかに簡単になります。

28
sircodesalot

2番目のアプローチは C#のオブジェクト初期化子

オブジェクト初期化子を使用すると、作成時にオブジェクトのアクセス可能なフィールドまたはプロパティに値を割り当てることができます。コンストラクターを明示的に呼び出す必要はありません

最初のアプローチ

var albumData = new Album("Albumius", "Artistus", 2013);

明示的にコンストラクターを呼び出しますが、2番目のアプローチではコンストラクター呼び出しは暗黙的です。オブジェクト初期化子を使用すると、一部のプロパティも除外できます。のような:

 var albumData = new Album
        {
            Name = "Albumius",
        };

オブジェクト初期化子は次のように変換されます。

var albumData; 
var temp = new Album();
temp.Name = "Albumius";
temp.Artist = "Artistus";
temp.Year = 2013;
albumData = temp;

一時オブジェクト(デバッグモード)を使用する理由は、Jon Skeetが here と答えています。

両方のアプローチの利点に関する限り、すべてのフィールドを初期化したくない場合、IMO、オブジェクト初期化子は特別に使いやすいでしょう。パフォーマンスの違いに関する限り、オブジェクト初期化子がパラメーターを使用しないコンストラクターを呼び出してからプロパティを割り当てるため、何も起こらないと思います。パフォーマンスに違いが生じる場合でも、無視できるはずです。

13
Habib