VB.NETやC#の構造体において、デフォルトコンストラクタを定義できない理由を教えてください。

引数付きのコンストラクタが定義できるのに、デフォルトコンストラクタが定義できないのが不思議です。

回答の条件
  • 1人10回まで
  • 登録:
  • 終了:2006/11/03 00:21:26
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答3件)

id:dasm No.1

回答回数66ベストアンサー獲得回数0

ポイント27pt

C# の言語仕様ですが、

http://www.csharpfriends.com/Spec/index.aspx?specID=11.1.1.htm

Because every value type implicitly has a public parameterless instance constructor, it is not possible for a struct type to contain an explicit declaration of a parameterless constructor.

つまり、全ての構造体は既に (implicitly) 引数なしのコンストラクタ (default constructor) が定義されているので、(explicit に) 定義できない、とあります。

http://www.csharpfriends.com/Spec/index.aspx?specID=18.3.8.htm

Unlike a class, a struct is not permitted to declare a parameterless instance constructor. 2 Instead, every struct implicitly has a parameterless instance constructor, which always returns the value that results from setting all value type fields to their default value and all reference type fields to null.

その代わり、そのデフォルトコンストラクタは全ての値をデフォルト値にセットし、リファレンスは null にする、とあります。

更に深い話は言語設計に携わっている人でないと難しいと思いますが (Microsoft のサイトを見れば見つかるかもしれません)、求めている回答でなければポイントはお返しします。

id:witt

ご回答いただいたC# の言語仕様については知っておりますので、更に深い話をお待ちしております。

2006/10/31 11:53:39
id:dasm No.2

回答回数66ベストアンサー獲得回数0

ポイント27pt

少しだけ深い話をしたいと思うのですが、

http://www.yoda.arachsys.com/csharp/faq/#struct.constructors

The .NET runtime can't guarantee that parameterless constructors will be called. If structs where to allow default, parameterless constructors, it would imply that these default constructors would *always* be called. However, the runtime can not make this guarantee. For example an array of value types will be initialized to the initial values of its members (i.e. 0 for number type primitive members, null for reference types etc) not to the values provided in a default constructor - which makes structs better performing by not having to call constructor code. Enforcing a minimum of one parameter in the constructor reduces the possibility that someone will define a constructor that they then expect to be called every time one of their struct types is constructed.

.NET Runtime では、デフォルトコンストラクタが呼ばれることを保証できません。もし許してしまえば常にデフォルトコンストラクタが呼ばれることになります。デフォルトコンストラクタを呼ばないことでパフォーマンスが上がります。引数を一つ以上に制限することで、コンストラクタの定義の手間を省くことができます、と言っています。

http://www.codeproject.com/csharp/structs_in_csharp.asp

Although the CLR allows it, C# does not allow structs to have a default parameterless constructor. The reason is that, for a value type, compilers by default neither generate a default constructor, nor do they generate a call to the default constructor. So, even if you happened to define a default constructor, it will not be called and that will only confuse you.

CLR ではデフォルトコンストラクタの定義を許しています。

C# の構造体では

struct MyStrstrcut

{

int i=10; //illegal

}

のようなフィールドの初期化が許されない為、必ず全てのフィールドを初期化する引数つきのコンストラクタを定義する必要がある。デフォルトコンストラクタを定義できるとユーザを混乱させてしまうから禁止しているんだ、と言っています。


ひょっとしたらもっと深い話を希望されているかもしれませんが、結局は言語設計者の方針として、コンストラクタの定義の手間を減らしたい、ユーザの混乱を減らしたい、パフォーマンスを上げたいということがあるからではないでしょうか。

id:witt

ありがとうございます。このレベルの深い話を期待してました。

ただ、.NET Runtime は CLR とILのライブラリを併せたものと考えていたので、

>.NET Runtime では、デフォルトコンストラクタが呼ばれることを保証できません。

>CLR ではデフォルトコンストラクタの定義を許しています。

の記述が矛盾しているように感じ、混乱しています。

2006/10/31 13:57:39
id:dasm No.3

回答回数66ベストアンサー獲得回数0

ポイント26pt

私は C# に精通しているわけではないので、詳しい説明は他に出来る方がいればその方に譲りたいのですが、先の回答で「CLR ではデフォルトコンストラクタの定義を許しています」としたのは、「クラス型」であり、「構造体」は「クラス型」に含まれないのではないでしょうか? (CLR についてよく知らないのでただの推測です)

http://ja.wikipedia.org/wiki/C_Sharp

構造体は「値型オブジェクト」とあります。CLI では「クラス型 (Class Type)」と「値型 (Value Type)」があり、「値型」である「構造体」はデフォルトコンストラクタに関する制約を受けないのではないかと思います。

CLI については巨大な pdf が公開されています。

http://www.ecma-international.org/publications/files/ECMA-ST/Ecm...

参考:

http://ja.wikipedia.org/wiki/.NET_Framework

http://www.ecma-international.org/publications/standards/Ecma-33...

id:witt

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

「CLR では(構造体ではなく、クラス型の)デフォルトコンストラクタの定義を許しています」

という解釈で良さそうですね。もう少し調べてみたいと思います。

2006/11/03 00:20:58
  • id:dasm
    三番目の回答は非常に誤解を招きやすいと思いますので、幾つか補足・訂正しておきます。

    「クラス型」と書きましたが、これは C++ や C#、Java などの "class" に相当するものを指すつもりで書きました。「オブジェクト指向」における一般的な「オブジェクト」のことです。しかし、CLI での「クラス型」はちょっと意味が違います。ECMA-335 の 17 ページ (PDF の 29 枚目) の図と、37 ページ (PDF の 49 枚目) の "Class type definition" を見てください。

    CLI での型は「Value type (値型)」と「Reference type (参照型)」に二分されます。三番目の回答の「クラス型」は CLI の世界では「参照型」と読み換えて頂くと正確に理解できると思います。

    そして、CLI での「クラス型」とは何かといいますと、33 ページ (PDF の 45 枚目) の "8.9 Type definers" にあるように、「Object type (オブジェクト型)」と「Value type (値型)」を定義するのに用いられます。「Object type (オブジェクト型)」は「Reference type (参照型)」に属すもので、その定義は 38 ページ (PDF 50 枚目) の "Object type definitions" にある通りです。

    つまり、CLI の世界では「クラス型」と言った場合、「構造体」も「クラス(オブジェクト)」も両方含んでしまうようです。「参照型」なら「クラス(オブジェクト)」のみ、「値型」なら「構造体」のみを含みます。「クラス」という言葉を不用意に用いると混乱しやすく、これは私のミスでした。すみません。

    ちなみに C# においては、「Reference type (参照型)」は全て class という予約語を用いて定義されるようです。C++/CLI では ref という予約語を用いるようです。

    C++/CLI 言語仕様書 12. 型
    http://vene.wankuma.com/ecma372/12_type.aspx

    また、もう一度元の質問に戻ると、値型のパフォーマンスの優位性については、以下の URL に記載されている内容が非常に参考になるかと思います。

    型についての再考:
    http://www.microsoft.com/japan/msdn/vs05/visualc/VS05Cplus.aspx#vs05cplus_topic6


    以下は参考資料です。

    値型:
    http://msdn2.microsoft.com/ja-jp/library/s1ax56ch(VS.80).aspx

    参照型:
    http://msdn2.microsoft.com/ja-jp/library/490f96s2(vs.80).aspx

    共通型システムの値型:
    http://msdn2.microsoft.com/ja-jp/library/34yytbws(VS.80).aspx

    共通型システムのクラス:
    http://msdn2.microsoft.com/ja-jp/library/2s9w552e(VS.80).aspx

    Class and struct differences:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_11_3.asp

    The C# Language:
    http://msdn2.microsoft.com/en-us/vcsharp/aa336809.aspx

    Microsoft シェアード ソース CLI 実装:
    http://www.microsoft.com/japan/msdn/net/sscli/mssharsourcecli.aspx

    改訂版 C#入門 第5章 C#のデータ型 5-6 値型と参照型
    http://www.atmarkit.co.jp/fdotnet/csharp_abc2/csabc2_005/cs2_005_03.html#cs0506

    改訂版 C#入門 第6章 変数と値型・参照型
    http://www.atmarkit.co.jp/fdotnet/csharp_abc2/csabc2_006/cs2_006_01.html
  • id:witt
    終始、詳細な説明をいただき、ありがとうございます。
    VB.NETとC#についての仕様の理解は自信があるのですが、CLIまで踏み込んだこととなると、これから…といった感じです。
    お示しいただいた参考資料をもとに勉強していきたいと思います。助かりました。
  • id:dasm
    最後に、構造体のデフォルトコンストラクタについて、これまでの CLI の知識を踏まえた上で補足させてください。

    http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf
    の 40 ページ (PDF 52 枚目) に "8.9.7 Value type definition" というセクションがあります。その中で

    5. Unlike object types, instances of value types do not require a constructor to be called when an instance is created. Instead, the verification rules require that verifiable code initialize instances to zero (null for object fields).

    とあります。つまり、Value type (値型) は、インスタンス化の際にコンストラクタを呼ぶのが必須ではないが、代わりに「何らかのルールでインスタンスがゼロになっている必要がある」…(*)、ということです。CLI には構造体という概念は恐らく無いと思いますが、構造体は値型ですので、構造体のコンストラクタも同様です。C# の構造体において (*) とは、「引数なしの (デフォルト) コンストラクタを定義できず、一つ以上引数を持つコンストラクタのみ定義できる」ということになります。

    また、37 ページ (PDF 49 枚目) の最後のパラグラフで、

    For reference types, a constructor has to be called to create a non-null instance. Thus, for reference types, the .cctor will be called before instance fields can be accessed and methods can be called on non-null instances. For value types, an “all-zero” instance can be created without a constructor (but only this value can be created without a constructor). Thus for value types, the .cctor is only guaranteed to be called for instances of the value type that are not “all-zero”. [Note: This changes the semantics slightly in the reference class case from the first edition of this standard, in that the .cctor might not be called before an instance method is invoked if the 'this' argument is null. The added performance of avoiding class constructors warrants this change. end note]

    という非常に重要な文章が書かれています。.cctor はユーザ定義の(静的)コンストラクタです。参照型の場合はユーザ定義の .cctor がインスタンス化の際に呼ばれる可能性があるが、値型の場合は .cctor は all-zero コンストラクタ (つまり、システム組み込みの引数なし値型コンストラクタ) を呼ばないときのみ呼ばれる、とあります。逆に、all-zero コンストラクタが呼ばれるときは .cctor は呼ばれないかもしれない、つまり、ユーザ定義の引数なしデフォルトコンストラクタは呼ばれる保証が無い、ということになります。また、Note においてパフォーマンスを上げたいという理由が書かれています。

    以上から、「CLR では (値型の) デフォルトコンストラクタの定義を許しています」は一応正しいことになります。但し、「オブジェクト型」のコンストラクタのような、全てのインスタンス生成時に必ず呼ばれる事が保証されないということです。定義は可能だがどうせ呼ばれない、ということです。

    そして、".NET Runtime" がどこまでのものを指しているかは正確に把握していませんが、「.NET Runtime では、デフォルトコンストラクタが呼ばれることを保証できません」も正しく、引数なしの場合は all-zero コンストラクタ (デフォルトコンストラクタ) が呼ばれ、それ以外は all-zero コンストラクタ (デフォルトコンストラクタ) を呼ぶことなく .cctor のみが呼ばれる、ということになるようです。
  • id:dasm
    すみません、一文だけ追加させてください。
    「CLR での値型コンストラクタの定義は (*) という曖昧な部分を含むが、C# では (*) を『ユーザ定義の引数なしコンストラクタ禁止』と具体化していて、C++/CLI などの別の言語では (*) を異なる仕様で実装しているかもしれない」、ということです。

この質問への反応(ブックマークコメント)

トラックバック

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません