none
値型って? RRS feed

  • 質問

  • 非常に初歩的なことで申し訳ないんですが、C#の構造体が値型だという事と、実態の定義の仕方の解釈が

    わからなくなってしまいました。

    struct st{

    int a;

    }

     

    の場合、

    st s;

    で実体が作られるようですが、stにメソッドや、プロパティがあると、newで生成しなければいけないようです。

    私の今までの解釈では、値型はnewで動的に生成しなくてもいいような気がしたのですが、間違いのようですね?

    ここは、どのように解釈すれば良いのでしょうか?

    2011年2月12日 8:55

回答

  • 値型のnewは動的にインスタンスを生成するものではなく、
    コンストラクタを呼び出しメンバーを初期化するだけのものです。
    コンストラクターで明示的に初期化しない場合も、
    デフォルトのコンストラクターで初期化された状態と同じになりますが、
    この場合はメンバー個別に値を設定する必要があります。
    値の設定に漏れがある場合は警告が出ますが、あくまでも警告です。
    なお配列やプロパティなどいくつかのケースでは
    この警告が発生しないことがあります。
    • 回答としてマーク 山本春海 2011年2月24日 8:43
    2011年2月12日 9:50
  • s 自体が未割り当てとされているわけではなくて、フィールドに未割り当てなものがあるので件のエラーが出るのは仕方がないと思います。
    さすがにコンパイラがメソッドの中身まで追っかけて構造体のフィールドを初期化しているかを確認してくれるわけではないですし。
    C# プログラミング ガイドにも次のような記述があります。

     構造体の使用 (C# プログラミング ガイド)
     http://msdn.microsoft.com/ja-jp/library/0taef578(VS.90).aspx

     クラスとは異なり、構造体は new 演算子を使用せずにインスタンス化できます。このような場合、コンストラクタの呼び出しが行われないため、割り当てがより効率的になります。ただし、各フィールドは未割り当てのままになり、すべてのフィールドが初期化されるまではオブジェクトを使用できません。

    st s;
    
    s.A(0);
    
    

    上記のパターンでも間に「s.a = 0;」などと明示的にフィールドを初期化してあげれば、コンパイル エラーは消えます。

    • 回答としてマーク 山本春海 2011年2月24日 8:43
    2011年2月12日 11:30
  • 例とちがってaをpublicにしています。

    struct st {
     public void A(int b){ a = b; }
     public int a;
    }

    以下の3つ、どちらでもコンパイルが通ります。

    st s; s.a = 0; s.A(0);

    st s; s.a = new int(); s.A(0);

    st s = new st(); s.A(0);

    new しない場合は同じプロシージャ内でメンバーを初期化する必要があります。
    別の場所(A関数)での初期化は認めていないようです。

    • 回答としてマーク 山本春海 2011年2月24日 8:43
    2011年2月12日 11:38

すべての返信

  • 値型のnewは動的にインスタンスを生成するものではなく、
    コンストラクタを呼び出しメンバーを初期化するだけのものです。
    コンストラクターで明示的に初期化しない場合も、
    デフォルトのコンストラクターで初期化された状態と同じになりますが、
    この場合はメンバー個別に値を設定する必要があります。
    値の設定に漏れがある場合は警告が出ますが、あくまでも警告です。
    なお配列やプロパティなどいくつかのケースでは
    この警告が発生しないことがあります。
    • 回答としてマーク 山本春海 2011年2月24日 8:43
    2011年2月12日 9:50
  • 回答ありがとうございます。

    >コンストラクターで明示的に初期化しない場合も、
    >デフォルトのコンストラクターで初期化された状態と同じになりますが、

     

    しかしながら、先の例で、stのメンバに

    public int A(int b){ a=b;}

    を追加して他の所で

    st s;

    s.A(0);

    などとすると、「未割り当てのローカル変数's'が使用されました。」と言われ、sが突然未割り当てとされるようなのです。

    他のコンパイラでも同様のメッセージが出るので、仕様なのでしょうが、これを説明した文章を見た事がないので、こちらでお聞きした次第です。

    これに関する説明をお聞きできれば幸いです。よろしくお願い致します。

    2011年2月12日 10:09
  • s 自体が未割り当てとされているわけではなくて、フィールドに未割り当てなものがあるので件のエラーが出るのは仕方がないと思います。
    さすがにコンパイラがメソッドの中身まで追っかけて構造体のフィールドを初期化しているかを確認してくれるわけではないですし。
    C# プログラミング ガイドにも次のような記述があります。

     構造体の使用 (C# プログラミング ガイド)
     http://msdn.microsoft.com/ja-jp/library/0taef578(VS.90).aspx

     クラスとは異なり、構造体は new 演算子を使用せずにインスタンス化できます。このような場合、コンストラクタの呼び出しが行われないため、割り当てがより効率的になります。ただし、各フィールドは未割り当てのままになり、すべてのフィールドが初期化されるまではオブジェクトを使用できません。

    st s;
    
    s.A(0);
    
    

    上記のパターンでも間に「s.a = 0;」などと明示的にフィールドを初期化してあげれば、コンパイル エラーは消えます。

    • 回答としてマーク 山本春海 2011年2月24日 8:43
    2011年2月12日 11:30
  • 例とちがってaをpublicにしています。

    struct st {
     public void A(int b){ a = b; }
     public int a;
    }

    以下の3つ、どちらでもコンパイルが通ります。

    st s; s.a = 0; s.A(0);

    st s; s.a = new int(); s.A(0);

    st s = new st(); s.A(0);

    new しない場合は同じプロシージャ内でメンバーを初期化する必要があります。
    別の場所(A関数)での初期化は認めていないようです。

    • 回答としてマーク 山本春海 2011年2月24日 8:43
    2011年2月12日 11:38
  • totojo様、ぱらぐあい様、回答ありがとうございます。

     

    なるほど、理解できたような気がします。

    つまり、構造体のメンバは必ず初期化しなければならず、new演算子は、初期化も同様にしてくれるわけですね。

    で、そうでない場合には、宣言したのと同じ場所で使用前に明示的に初期化しないといけないんですね。

     

     

    ご教授ありがとうございました。

     

    2011年2月12日 12:31