18

このようなクラスがある場合:

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; }

    public Foo()
    {
        Bars = new List<Bar>();
    }
}

ある段階で、クラスをリファクタリングし、最初のコンストラクターを次のように実装するセカンダリ コンストラクターを追加します。

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; }

    // some more properties were added

    public Foo()
    {
        Bars = new List<Bar>();
    }

    public Foo(string parameter): this()
    {
        .... some code here
    }
}

次のように書くこともできます。

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; }

    // some more properties were added too

    public Foo()
    {
        InitilizeFoo();
    }

    public Foo(string parameter)
    {
        InitilizeFoo();
        .... some code here
    }

    private void InitializeFoo()
    {
        Bars = new List<Bar>();
    }
}

このシナリオで両方のアプローチが機能するのを見て、一方を他方よりも使用する利点または欠点はありますか?

コンストラクターを継承する方が効率的で、そのコードをより高速に実行できますか、それとも 2 番目の実装をより効率的にすることについて私が知らない欠点がありますか?

4

5 に答える 5

28

あるコンストラクターに別のコンストラクターを呼び出させることの主な利点の1つは、読み取り専用フィールドをそのように設定できることです。コンストラクター以外のメソッドを呼び出すことによってそれを行うことはできません。

例えば:

public class Foo
{
    private readonly int myNumber;

    public Foo() : this(42)
    {
    }

    public Foo(int num)
    {
        myNumber = num;
    }
}

パフォーマンスの面では、別のコンストラクターを呼び出す方が別のメソッドを呼び出すよりも多かれ少なかれ効率的ですが、私の意見では、コンストラクターが別のコンストラクターを呼び出す方が、別のプライベートメソッドを呼び出すよりも読みやすくなります。コンストラクターによって呼び出されます。

もちろん、別の方法を使用することが理にかなっている状況もあり得ますが、それ自体は確かに「間違っている」わけではありません。コンストラクターの連鎖は、ほとんどの用途で多くの人に読みやすく、パフォーマンスへの悪影響はありません。

更新:各方法(連鎖初期化方法とプライベート初期化方法)を10,000,000回繰り返しましたが、結果は非常に近く、ほとんど区別できませんでした。

Initializer Method took: 84 ms for 10,000,000 iterations, 8.4E-06 ms/each.
Chained Constructors took: 81 ms for 10,000,000 iterations, 8.1E-06 ms/each.

したがって、実際には、パフォーマンスの面では、どちらの方法でもほとんどメリットはありません。主な利点は、フィールドを設定できるチェーンコンストラクターを使用することreadonlyであり、ほとんどの場合、より読みやすくなります。

于 2012-05-31T17:21:56.303 に答える
6

コンストラクターの連鎖は、SRPとプログラムフローを強制するための良い方法です。Initialize()オブジェクトのライフサイクルに「初期化」する必要がある他の状況がある場合は、スタンドアロン関数内に初期化コードを非表示にすることは理にかなっています。おそらく、それをすばやくインスタンス化して遅延初期化できるようにしたい場合。ただし、ライフサイクルでその機能を実行する唯一の有効な時間がインスタンス化中であり、初期化が順番に実行する必要のある明確に定義された一連の個別のステップである場合、チェーンはそれを容易にします。

于 2012-05-31T17:23:21.043 に答える
4

重要なのは、重複するコードの量を減らすことです。この場合、パラメーター化されたコンストラクターから基本コンストラクターを呼び出すと、両方を更新するのを忘れた後でバグを追加する可能性が低くなります。

于 2012-05-31T17:22:15.493 に答える
3

Initialise()関数を使用する利点は、オブジェクトをリセットする場合です。オブジェクトを削除して再作成するのではなく、init関数を再度呼び出すだけです。

于 2012-05-31T17:23:22.730 に答える
3

これを言うとやけどするかもしれませんが、この場合はデフォルトのパラメーターを使用することをお勧めします。

public Foo(string parameter = null)
{

}

10 ~ 15 個のオプションのパラメーターがあり、15 個の異なるコンストラクターを持つことは、私の意見ではエレガントなソリューションではありませんでした。ただし、デフォルトのパラメーターは 4.0 フレームワークでのみ再導入されたと思います。

于 2012-05-31T17:26:10.567 に答える