Why is it possible to instantiate a struct without

2019-01-16 09:40发布

问题:

Why are we not forced to instantiate a struct, like when using a class?

回答1:

The why is simply - because the spec says so. The how is a matter of ensuring that the entire block of memory is "definitely assigned", which means: assigning a value to each field of the struct. However, this requires 2 nasty things:

  • public fields (almost always bad)
  • mutable fields (generally bad in a struct)

so in most best-practice cases, you do need to use the new(...) syntax, to invoke the constructor (or to zero-the memory, for the parameterless constructor) for the type correctly.



回答2:

Why are we not forced to instantiate a struct with "new", like when using a class?

When you "new" a reference type, three things happen. First, the memory manager allocates space from long term storage. Second, a reference to that space is passed to the constructor, which initializes the instance. Third, that reference is passed back to the caller.

When you "new" a value type, three things happen. First, the memory manager allocates space from short term storage. Second, the constructor is passed a reference to the short term storage location. After the constructor runs, the value that was in the short-term storage location is copied to the storage location for the value, wherever that happens to be. Remember, variables of value type store the actual value.

(Note that the compiler is allowed to optimize these three steps into one step if the compiler can determine that doing so never exposes a partially-constructed struct to user code. That is, the compiler can generate code that simply passes a reference to the final storage location to the constructor, thereby saving one allocation and one copy.)

So now we can address your question, which you actually have asked backwards. It would be better to ask:

Why are we forced to allocate a class with "new", instead of simply being able to initialize the fields as with a struct?

You have to allocate a class with "new" because of those three things on the list. You need new memory allocated from the long-term storage and you need to pass a reference to that storage to the constructor. "new" is the operator that knows how to do that.

You don't have to call "new" on a struct because there is no need to allocate the "final" storage; the final storage already exists. The new value is going to go somewhere, and you already have obtained that storage by some other means. Value types do not need a new allocation; all they need is initialization. All you need to do is ensure that the storage is properly initialized, and you can often do that without calling a constructor. Doing so of course means that you run the risk of having a variable of value type that can be observed to be in a partially initialized state by user code.

Summing up: calling a ctor is optional for value types because no new memory needs to be allocated when initializing an instance of a value type and because skipping the constructor call means that you get to skip a short-term allocation and a copy. The price you pay for that performance gain is that user code can see a partially initialized structure.



回答3:

Because structs are value types and classes are reference types. So structs fall into the same category as int, double etc.



回答4:

Because a struct is a value-type. When you declare a variable of it, the instance is immediateley there.

A constructor (the new operator) is therefore optional for a struct.

Consider

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

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

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

   b = new R();  // allocates new memory
   a = new V();  // overwrites memory

}


回答5:

Coming in a year and a half later...

A struct is usually passed by value, while a class is always passed by reference. You probably have a good understanding of what happens when an object is passed by reference. When an object is passed by value, its contents, rather than the object itself, are passed along. To the programmer, it appears as though a shallow copy of the object is made. Changing one instance won't change the other.

All variables (including fields and properties) always have space allocated for them as long as they exist. It's important to note that local variables don't exist until a value is assigned to them in newer versions of C#. In the case of class-type variables, the allocated space will contain a reference to the contents of the object. In the case of a struct-type variable, the allocated space will contain the actual contents of the object.

So, let's say you have a an "empty" class-type variable. It will have a default reference. That reference will equate to null. However, a struct-type variable isn't a reference: it's the actual contents of an object. When left "empty", all of its fields (and auto-implemented properties, which are backed by fields behind-the-scenes) all contain default values--in other words, they are also "empty". If they are reference types, they'll be null; if they are value types, they'll be 0, or zeroed structs (and the chain continues).

This is also why a structs cannot have a default constructor. Just as you cannot override what a class looks like when it is null, you cannot override what a struct looks like when it is zeroed.

There is an underused operator for obtaining the default value of any type--class, struct, or intrinsic. That is the default() operator. For example:

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);
}


回答6:

As said by David Heffernan and Henk Holterman its because structs are value types and hence instantiates while declaring it. For a better understanding about ValueType and ReferenceType, please check this link P Daddy has nicely explained it.



回答7:

Additionally to what was posted: Note that a struct cannot have a parameterless constructor or have a initializer for any of its instance fields. The default value is having all value type fields set to their default value (ex. 0 for ints, false for bool, etc.) and all reference type fields to null.

Secondly, a struct is initialized, for example by calling a constructor or using default().