12

重複の可能性:
ベスト プラクティス: コンストラクターまたは宣言時にクラス フィールドを初期化しますか?

ほとんどの場合、このような変数を初期化する方法を目にします

public class Test
{
    private int myIntToInitalize;

    public Test()
    {
        myIntToInitalize = 10;
    }
}

私の見解では、これが変数を初期化する最も一般的な方法です。書籍、ブログ、および .NET の内部実装のほとんどのコードは、私の例と同等です。

最近、コンストラクターで値を設定せずに、初期化を直接行っている人を見ました。

public class Test
{
    private int myIntToInitalize = 10;
}

観点では、変数を初期化して宣言するか、コンストラクターで変数を初期化するかに違いはありません。

ベスト プラクティスとコード行の長さは別として、変数を直接初期化する利点はどこにあり、微妙な違いはありますか?

4

6 に答える 6

24

場合によっては、潜在的に重要な違いが 1 つあります。

インスタンス初期化子は、基底クラスのコンストラクターが実行される前に実行されます。したがって、基本クラスのコンストラクターが、派生クラスでオーバーライドされる仮想メソッドを呼び出す場合、そのメソッドには違いが見られます。ただし、通常、これは顕著な違いではないはずです。コンストラクターで仮想メソッドを呼び出すことは、ほとんどの場合、悪い考えであるためです。

明確にするために、宣言の時点で変数を初期化すると、値がコンストラクターのパラメーターに依存しないことが非常に明確になります。一方、すべての初期化をまとめておくと、IMO の読みやすさも向上します。可能な限り、複数のコンストラクターがある場合は、すべての「実際の」初期化を行う 1 つの「マスター」コンストラクターにすべて委任するようにします。つまり、これらの割り当てをいずれかの場所にのみ配置します

違いを示すサンプル コード:

using System;

class Base
{
    public Base()
    {
        Console.WriteLine(ToString());
    }
}

class Derived : Base
{
    private int x = 5;
    private int y;

    public Derived()
    {
        y = 5;
    }

    public override string ToString()
    {
        return string.Format("x={0}, y={1}", x, y);
    }
}

class Test
{
    static void Main()
    {
        // Prints x=5, y=0
        new Derived();
    }
}
于 2012-12-29T16:49:46.507 に答える
5

微妙な違いはわかりません。コードが読みやすくなると思うので、私は通常、すべての初期化をコンストラクターに入れるのが好きです。しかし、それはスタイルの選択と個人的な好みです。何らかの理由でそれを正当化する技術的な理由を聞いたことがありません。パフォーマンスへの影響はないと思います。

静的定数と最終定数は別の問題です。それらをインラインで初期化します。

于 2012-12-29T16:40:26.250 に答える
4

インスタンスが構築されると、宣言時に初期化される変数は、コンストラクターが実行される前に初期化されます。これらの変数にアクセスしていない場合、またはコンストラクター自体でそれらの値を使用していない場合、2つのメソッドの間に機能的な違いはありません。

于 2012-12-29T16:41:14.443 に答える
3

リリースと最適化されたビルドの下で、次のコードをコンパイルするとします

namespace ConsoleApplication4
{

    public class Test1
    {
        private int myIntToInitalize;

        public Test1()
        {
            myIntToInitalize = 10;
        }
    }

    public class Test2
    {
        private int myIntToInitalize = 10;
    }


    static class Program
    {

        private static void Main()
        {
        }


    }

}

クラステスト1のIL命令

.class public auto ansi beforefieldinit Test1
    extends [mscorlib]System.Object
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
        .maxstack 8
        L_0000: ldarg.0 
        L_0001: call instance void [mscorlib]System.Object::.ctor()
        L_0006: ldarg.0 
        L_0007: ldc.i4.s 10
        L_0009: stfld int32 ConsoleApplication4.Test1::myIntToInitalize
        L_000e: ret 
    }


    .field private int32 myIntToInitalize

}

クラステスト2のIL命令

.class public auto ansi beforefieldinit Test2
    extends [mscorlib]System.Object
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
        .maxstack 8
        L_0000: ldarg.0 
        L_0001: ldc.i4.s 10
        L_0003: stfld int32 ConsoleApplication4.Test2::myIntToInitalize
        L_0008: ldarg.0 
        L_0009: call instance void [mscorlib]System.Object::.ctor()
        L_000e: ret 
    }


    .field private int32 myIntToInitalize

}

両方のクラスが同じ数のIL命令を持っていることは非常に明白ですが、唯一の違いは、

変数は、クラスTest1の:: ctor()を呼び出す前に初期化されます。クラスTest2で::ctor()を呼び出した後、変数が初期化されます。

:パフォーマンスに関しては、両方のクラスが同じように実行されます。IL命令の数とタイプが同じであるため、IL命令の実行順序が異なるだけです。

于 2012-12-29T17:08:39.140 に答える
2

あなたの単純な例では、それは単にスタイルの問題です。

継承が関係する場合、微妙な違いがあります。インスタンス フィールド初期化子の実行順序は、派生クラス フィールド初期化子、基本クラス フィールド初期化子、基本クラス コンストラクター、派生クラス コンストラクターです。

このサンプルを見てください:

public class Program
{
    public static void Main(string[] args)
    {
        new Derived();
    }
 }

 public class Base 
 {
     private int x = BaseInitializer();

     public Base()
     {
          Console.WriteLine("Base ctor");
     }

     private static int BaseInitializer()
     {
         Console.WriteLine("BaseInitializer");
         return 0;
     }
 }

 public class Derived : Base
 {
     private int x = DerivedInitializer();

     public Derived() : base()
     {
         Console.WriteLine("Derived ctor");
     }

     private static int DerivedInitializer()
     {
         Console.WriteLine("DerivedInitializer");
         return 0;
     }
 }

それは印刷します:

  • DerivedInitializer
  • BaseInitializer
  • ベースセクター
  • 派生ctor
于 2012-12-29T16:51:08.180 に答える
0
public class Test
{
    private int myIntToInitalize = 10;
    public Test()
    {

    }

    public Test(string x)
    {
    }
}

public class Test2
{
    private int myIntToInitalize;
    public Test2()
    {
        this.myIntToInitalize = 10;
    }

    public Test2(string x)
    {
    }
}

クラス Test1 では、すべてのオブジェクト インスタンスに myIntToInitalize が 10 に設定されますが、1 つのパラメーターを必要とするコンストラクターを呼び出すと、クラス Test2 では設定されません。

于 2012-12-29T16:54:30.187 に答える