web-dev-qa-db-ja.com

新しいキーワードなしで構造体をインスタンス化できるのはなぜですか?

クラスを使用するときのように、構造体をインスタンス化する必要がないのはなぜですか?

23
Birdman

whyは単純に-仕様にそのように記載されているためhowは、メモリのブロック全体が「確実に割り当てられる」ことを保証する問題です。つまり、構造体の各フィールドに値を割り当てます。ただし、これには2つの厄介なことが必要です。

  • パブリックフィールド(ほとんどの場合悪い)
  • 可変フィールド(一般的に構造体では悪い)

したがって、ほとんどのベストプラクティスの場合doを使用する必要がありますnew(...)構文、型のコンストラクターを正しく呼び出す(またはゼロ-パラメーターなしのコンストラクターの場合はメモリー)。

19
Marc Gravell

クラスを使用するときのように、なぜ「new」で構造体をインスタンス化する必要がないのですか?

参照型を「新規」にすると、3つのことが起こります。まず、メモリマネージャは長期ストレージからスペースを割り当てます。次に、そのスペースへの参照がコンストラクターに渡され、コンストラクターがインスタンスを初期化します。第三に、その参照は呼び出し元に返されます。

値型を「新規」にすると、3つのことが起こります。まず、メモリマネージャは短期ストレージからスペースを割り当てます。次に、コンストラクターに短期保管場所への参照が渡されます。コンストラクターの実行後、短期保管場所にあった値は、その値の保管場所にコピーされます。 値型の変数は実際の値を格納するであることを忘れないでください。

(コンパイラーは、部分的に構築された構造体をユーザーコードに公開しないとコンパイラーが判断できる場合、これらの3つのステップを1つのステップに最適化できます。つまり、コンパイラーは、-への参照を渡すだけのコードを生成できます。 finalコンストラクターへの格納場所。これにより、1つの割り当てと1つのコピーが節約されます。)

これで、実際に逆に質問した質問に対処できます。質問したほうがいいでしょう:

構造体のように単にフィールドを初期化できるのではなく、なぜ「new」でクラスを割り当てる必要があるのですか?

リストにあるこれらの3つの理由から、「新規」のクラスを割り当てる必要があります。 長期ストレージから割り当てられた新しいメモリが必要であり、そのストレージへの参照をコンストラクターに渡す必要があります。 「新規」とは、その方法を知っている演算子です。

「最終」ストレージを割り当てる必要がないため、構造体で「新規」を呼び出す必要はありません。 最終ストレージはすでに存在します。新しい値はどこかになり、他の方法でそのストレージをすでに取得しています。 値型には新しい割り当ては必要ありません。必要なのは初期化だけです。必要なのはストレージが適切に初期化されているであることを確認することだけです。コンストラクターの呼び出し。 もちろんそうすることは、ユーザーコードによって部分的に初期化された状態にあることが観察できる値型の変数を持つリスクを冒すことを意味します。

要約すると、ctorの呼び出しは値型ではオプションです。これは、値型のインスタンスを初期化するときに新しいメモリを割り当てる必要がないため、コンストラクター呼び出しをスキップすると、スキップできるためです。短期間の割り当てとコピー。そのパフォーマンスの向上に対して支払う代償は、ユーザーコードは部分的に初期化された構造を見ることができる

50
Eric Lippert

構造体は値型だからです。その変数を宣言すると、インスタンスはすぐそこにあります。

したがって、コンストラクター(new演算子)は構造体ではオプションです。

検討する

struct V { public int x; }
class  R { public int y = 0; }

void F() 
{
   V a;   // a is an instance of V, a.x is unassigned  
   R b;   // b is a reference to an R

   a.x = 1; // OK, the instance exists
 //b.y = 2; // error, there is no instance yet

   a = new V();  // overwrites the memory of 'a'. a.x == 0
   b = new R();  // allocates new memory on the Heap

   b.y = 2; // now this is OK, b points to an instance
}
11
Henk Holterman

構造体は値型であり、クラスは参照型であるためです。したがって、構造体はint、doubleなどと同じカテゴリに分類されます。

10
David Heffernan

1年半後に来る...

struct通常値で渡されますが、class常に参照で渡されます。おそらく、オブジェクトが参照によって渡されたときに何が起こるかをよく理解しているでしょう。オブジェクトが値で渡されると、オブジェクトへの参照ではなく、その内容が渡されます。プログラマーには、オブジェクトの浅いコピーが作成されているように見えます。一方のインスタンスを変更しても、もう一方は変更されません。

すべての変数(フィールドとプロパティを含む)には、それらが存在する限り、常にスペースが割り当てられます。 local変数は、新しいバージョンのC#で値が割り当てられるまで存在しないことに注意することが重要です。 class-type変数の場合、割り当てられたスペースには、オブジェクトのコンテンツへの参照が含まれます。 struct-type変数の場合、割り当てられたスペースには、オブジェクトの実際の内容が含まれます。

したがって、「空の」class型変数があるとします。デフォルトの参照があります。その参照はnullに相当します。ただし、struct-type変数は参照ではなく、オブジェクトの実際の内容です。 「空」のままにすると、そのすべてのフィールド(および舞台裏のフィールドに裏打ちされた自動実装プロパティ)にはすべてデフォルト値が含まれます。つまり、これらも「空」になります。それらが参照型である場合、それらはnullになります。それらが値型である場合、それらは0またはゼロの構造体になります(そしてチェーンは継続します)。

これが、structsがデフォルトのコンストラクターを持つことができない理由でもあります。 classnullの場合の外観をオーバーライドできないのと同様に、structがゼロの場合の外観をオーバーライドすることはできません。

任意のタイプのデフォルト値を取得するための十分に活用されていない演算子があります--classstruct、または組み込み。それがdefault()演算子です。例えば:

class ClassType { }
struct StructType { }

//
// ...
//

var classA = default(ClassType);
var classB = (ClassType)null;

if (classA == classB)
{
    // This will execute, because both equal null.
}

var structA = default(StructType);
var structB = new StructType();

if (structA == structB)
{
    // This will execute, because both are zeroed.
}

//
// ...
//

/// <summary>
/// An example use case for the <c>default()</c> operator.
/// </summary>
/// <returns>
/// <c>null</c> if <c>T</c> is a reference type, a zeroed instance <c>T</c> is a 
/// <c>struct</c>, or <c>0</c> if <c>T</c> is an intrinsic type.
/// </returns>
private static T GetDefault<T>()
{
    // This line wouldn't compile, because T could be a value type.
    //return null;

    // This line wouldn't compile, because T could be a reference type without a default or accessible constructor.
    //return new T();

    // This will work!
    return default(T);

    // In newer versions of C#, when the type is known from the context, it can be omitted:
    //return default;
}
2
Zenexer

David HeffernanHenk Holtermanが言ったように、構造体は値型だからですしたがって、それを宣言しながらインスタンス化します。 ValueTypeとReferenceTypeの理解を深めるには、 このリンクP Daddyがうまく説明していることを確認してください。

0
NaveenBhat

投稿された内容に加えて:構造体は、パラメーターのないコンストラクターを持つことも、そのインスタンスフィールドのいずれかの初期化子を持つこともできないことに注意してください。デフォルト値では、すべての値型フィールドがデフォルト値に設定され(たとえば、intの場合は0、boolの場合はfalseなど)、すべての参照型フィールドがnullに設定されます。

次に、コンストラクターを呼び出すか、default()を使用して、構造体を初期化します。

0
Daniel Rose