关键字“new”对 C# 中的结构有什么作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9207488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What does the keyword "new" do to a struct in C#?
提问by KMC
In C#, Structs are managed in terms of values, and objects are in reference. From my understanding, when creating an instance of a class, the keyword newcauses C# to use the class information to make the instance, as in below:
在 C# 中,Structs 是根据值来管理的,而对象是在引用中。根据我的理解,在创建类的实例时,关键字new会导致C#使用类信息来创建实例,如下所示:
class MyClass
{
...
}
MyClass mc = new MyClass();
For struct, you're not creating an object but simply set a variable to a value:
对于 struct,您不是在创建对象,而只是将变量设置为一个值:
struct MyStruct
{
public string name;
}
MyStruct ms;
//MyStruct ms = new MyStruct();
ms.name = "donkey";
What I do not understand is if declare variables by MyStruct ms = new MyStruct(), what is the keyword newhere is doing to the statement? . If struct cannot be an object, what is the newhere instantiating?
我不明白的是,如果通过声明变量MyStruct ms = new MyStruct(),new这里的关键字对语句有什么作用?. 如果 struct 不能是对象,那么new这里的实例化是什么?
采纳答案by joshhendo
From struct (C# Reference)on MSDN:
从struct (C# Reference)MSDN 上:
When you create a struct object using the new operator, it gets created and the appropriate constructor is called. Unlike classes, structs can be instantiated without using the new operator. If you do not use new, the fields will remain unassigned and the object cannot be used until all of the fields are initialized.
当您使用 new 运算符创建结构对象时,它会被创建并调用适当的构造函数。与类不同,结构体可以在不使用 new 运算符的情况下实例化。如果不使用 new,则字段将保持未分配状态,并且在所有字段都初始化之前无法使用该对象。
To my understanding, you won't actually be able to use a struct properly without using newunless you make sure you initialise all the fields manually. If you use the new operator, then a properly-written constructor has the opportunity to do this for you.
据我了解,除非您确保手动初始化所有字段,否则您实际上无法在不使用new 的情况下正确使用结构。如果您使用 new 运算符,那么正确编写的构造函数就有机会为您执行此操作。
Hope that clears it up. If you need clarification on this let me know.
希望能解决这个问题。如果您需要对此进行澄清,请告诉我。
Edit
编辑
There's quite a long comment thread, so I thought I'd add a bit more here. I think the best way to understand it is to give it a go. Make a console project in Visual Studio called "StructTest" and copy the following code into it.
有很长的评论线程,所以我想我会在这里添加更多。我认为理解它的最好方法是试一试。在 Visual Studio 中创建一个名为“StructTest”的控制台项目,并将以下代码复制到其中。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace struct_test
{
class Program
{
public struct Point
{
public int x, y;
public Point(int x)
{
this.x = x;
this.y = 5;
}
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
// It will break with this constructor. If uncommenting this one
// comment out the other one with only one integer, otherwise it
// will fail because you are overloading with duplicate parameter
// types, rather than what I'm trying to demonstrate.
/*public Point(int y)
{
this.y = y;
}*/
}
static void Main(string[] args)
{
// Declare an object:
Point myPoint;
//Point myPoint = new Point(10, 20);
//Point myPoint = new Point(15);
//Point myPoint = new Point();
// Initialize:
// Try not using any constructor but comment out one of these
// and see what happens. (It should fail when you compile it)
myPoint.x = 10;
myPoint.y = 20;
// Display results:
Console.WriteLine("My Point:");
Console.WriteLine("x = {0}, y = {1}", myPoint.x, myPoint.y);
Console.ReadKey(true);
}
}
}
Play around with it. Remove the constructors and see what happens. Try using a constructor that only initialises one variable(I've commented one out... it won't compile). Try with and without the newkeyword(I've commented out some examples, uncomment them and give them a try).
玩弄它。删除构造函数,看看会发生什么。尝试使用只初始化一个变量的构造函数(我已经注释掉了一个......它不会编译)。尝试使用和不使用new关键字(我已经注释掉了一些示例,取消注释并试一试)。
回答by Daryl
Using "new MyStuct()" ensures that all fields are set to some value. In the case above, nothing is different. If instead of setting ms.name you where trying to read it, you would get a "Use of possible unassigned field 'name'" error in VS.
使用“new MyStuct()”确保所有字段都设置为某个值。在上述情况下,没有什么不同。如果不是将 ms.name 设置为您尝试阅读它的位置,您将在 VS 中收到“使用可能未分配的字段‘名称’”错误。
回答by supercat
Any time an object or struct comes into existence, all of its fields come into existence as well; if any of those fields are struct types, all nested fields come into existence as well. When an array is created, all of its elements come into existence (and, as above, if any of those elements are structs, the fields of those structs also come into existence). All of this occurs before any constructor code has a chance to run.
每当一个对象或结构出现时,它的所有字段也会出现;如果这些字段中的任何一个是结构类型,则所有嵌套字段也会存在。创建数组时,它的所有元素都存在(并且,如上所述,如果这些元素中的任何一个是结构,则这些结构的字段也会存在)。所有这些都发生在任何构造函数代码有机会运行之前。
In .net, a struct constructor is effectively nothing more than a method which takes a struct as an 'out' parameter. In C#, an expression which calls a struct constructor will allocate a temporary struct instance, call the constructor on that, and then use that temporary instance as the value of the expression. Note that this is different from vb.net, where the generated code for a constructor will start by zeroing out all fields, but where the code from the caller will attempt to have the constructor operate directly upon the destination. For example: myStruct = new myStructType(whatever)in vb.net will clear myStructbefore the first statement of the constructor executes; within the constructor, any writes to the object under construction will immediately operate upon myStruct.
在 .net 中,结构构造函数实际上只不过是一种将结构作为“输出”参数的方法。在 C# 中,调用 struct 构造函数的表达式将分配一个临时 struct 实例,在其上调用构造函数,然后将该临时实例用作表达式的值。请注意,这与 vb.net 不同,在 vb.net 中,构造函数的生成代码将从将所有字段清零开始,但调用方的代码将尝试让构造函数直接对目标进行操作。例如:myStruct = new myStructType(whatever)在vb.net中会myStruct在构造函数的第一条语句执行前清除;在构造函数中,对正在构造的对象的任何写入都将立即对 进行操作myStruct。
回答by Ken Kin
ValueTypeand structures are something special in C#. Here I'm showing you what happens when you newsomething.
ValueType和结构在 C# 中有些特殊。在这里,我向你展示了当你有新东西时会发生什么。
Here we have the following
这里我们有以下内容
Code
partial class TestClass { public static void NewLong() { var i=new long(); } public static void NewMyLong() { var i=new MyLong(); } public static void NewMyLongWithValue() { var i=new MyLong(1234); } public static void NewThatLong() { var i=new ThatLong(); } } [StructLayout(LayoutKind.Sequential)] public partial struct MyLong { const int bits=8*sizeof(int); public static implicit operator int(MyLong x) { return (int)x.m_Low; } public static implicit operator long(MyLong x) { long y=x.m_Hi; return (y<<bits)|x.m_Low; } public static implicit operator MyLong(long x) { var y=default(MyLong); y.m_Low=(uint)x; y.m_Hi=(int)(x>>bits); return y; } public MyLong(long x) { this=x; } uint m_Low; int m_Hi; } public partial class ThatLong { const int bits=8*sizeof(int); public static implicit operator int(ThatLong x) { return (int)x.m_Low; } public static implicit operator long(ThatLong x) { long y=x.m_Hi; return (y<<bits)|x.m_Low; } public static implicit operator ThatLong(long x) { return new ThatLong(x); } public ThatLong(long x) { this.m_Low=(uint)x; this.m_Hi=(int)(x>>bits); } public ThatLong() { int i=0; var b=i is ValueType; } uint m_Low; int m_Hi; }
代码
partial class TestClass { public static void NewLong() { var i=new long(); } public static void NewMyLong() { var i=new MyLong(); } public static void NewMyLongWithValue() { var i=new MyLong(1234); } public static void NewThatLong() { var i=new ThatLong(); } } [StructLayout(LayoutKind.Sequential)] public partial struct MyLong { const int bits=8*sizeof(int); public static implicit operator int(MyLong x) { return (int)x.m_Low; } public static implicit operator long(MyLong x) { long y=x.m_Hi; return (y<<bits)|x.m_Low; } public static implicit operator MyLong(long x) { var y=default(MyLong); y.m_Low=(uint)x; y.m_Hi=(int)(x>>bits); return y; } public MyLong(long x) { this=x; } uint m_Low; int m_Hi; } public partial class ThatLong { const int bits=8*sizeof(int); public static implicit operator int(ThatLong x) { return (int)x.m_Low; } public static implicit operator long(ThatLong x) { long y=x.m_Hi; return (y<<bits)|x.m_Low; } public static implicit operator ThatLong(long x) { return new ThatLong(x); } public ThatLong(long x) { this.m_Low=(uint)x; this.m_Hi=(int)(x>>bits); } public ThatLong() { int i=0; var b=i is ValueType; } uint m_Low; int m_Hi; }
And the generated IL of the methods of the test class would be
生成的测试类方法的 IL 将是
IL
// NewLong .method public hidebysig static void NewLong () cil managed { .maxstack 1 .locals init ( [0] int64 i ) IL_0000: nop IL_0001: ldc.i4.0 // push 0 as int IL_0002: conv.i8 // convert the pushed value to long IL_0003: stloc.0 // pop it to the first local variable, that is, i IL_0004: ret } // NewMyLong .method public hidebysig static void NewMyLong () cil managed { .maxstack 1 .locals init ( [0] valuetype MyLong i ) IL_0000: nop IL_0001: ldloca.s i // push address of i IL_0003: initobj MyLong // pop address of i and initialze as MyLong IL_0009: ret } // NewMyLongWithValue .method public hidebysig static void NewMyLongWithValue () cil managed { .maxstack 2 .locals init ( [0] valuetype MyLong i ) IL_0000: nop IL_0001: ldloca.s i // push address of i IL_0003: ldc.i4 1234 // push 1234 as int IL_0008: conv.i8 // convert the pushed value to long // call the constructor IL_0009: call instance void MyLong::.ctor(int64) IL_000e: nop IL_000f: ret } // NewThatLong .method public hidebysig static void NewThatLong () cil managed { // Method begins at RVA 0x33c8 // Code size 8 (0x8) .maxstack 1 .locals init ( [0] class ThatLong i ) IL_0000: nop // new by calling the constructor and push it's reference IL_0001: newobj instance void ThatLong::.ctor() // pop it to the first local variable, that is, i IL_0006: stloc.0 IL_0007: ret }
伊利诺伊州
// NewLong .method public hidebysig static void NewLong () cil managed { .maxstack 1 .locals init ( [0] int64 i ) IL_0000: nop IL_0001: ldc.i4.0 // push 0 as int IL_0002: conv.i8 // convert the pushed value to long IL_0003: stloc.0 // pop it to the first local variable, that is, i IL_0004: ret } // NewMyLong .method public hidebysig static void NewMyLong () cil managed { .maxstack 1 .locals init ( [0] valuetype MyLong i ) IL_0000: nop IL_0001: ldloca.s i // push address of i IL_0003: initobj MyLong // pop address of i and initialze as MyLong IL_0009: ret } // NewMyLongWithValue .method public hidebysig static void NewMyLongWithValue () cil managed { .maxstack 2 .locals init ( [0] valuetype MyLong i ) IL_0000: nop IL_0001: ldloca.s i // push address of i IL_0003: ldc.i4 1234 // push 1234 as int IL_0008: conv.i8 // convert the pushed value to long // call the constructor IL_0009: call instance void MyLong::.ctor(int64) IL_000e: nop IL_000f: ret } // NewThatLong .method public hidebysig static void NewThatLong () cil managed { // Method begins at RVA 0x33c8 // Code size 8 (0x8) .maxstack 1 .locals init ( [0] class ThatLong i ) IL_0000: nop // new by calling the constructor and push it's reference IL_0001: newobj instance void ThatLong::.ctor() // pop it to the first local variable, that is, i IL_0006: stloc.0 IL_0007: ret }
The behaviour of the methods are commented in the IL code. And you might want to take a look of OpCodes.Initobjand OpCodes.Newobj. The value type is usually initialized with OpCodes.Initobj, but as MSDN says OpCodes.Newobjwould also be used.
这些方法的行为在 IL 代码中进行了注释。你可能想看看OpCodes.Initobj和OpCodes.Newobj。值类型通常使用OpCodes.Initobj初始化,但正如 MSDN 所说,也将使用OpCodes.Newobj。
description in OpCodes.Newobj
Value types are not usuallycreated using newobj. They are usually allocated either as arguments or local variables, using newarr (for zero-based, one-dimensional arrays), or as fields of objects. Once allocated, they are initialized using Initobj. However, the newobj instruction can be used to create a new instance of a value type on the stack, that can then be passed as an argument, stored in a local, and so on.
值类型通常不是使用 newobj 创建的。它们通常被分配为参数或局部变量,使用 newarr(用于从零开始的一维数组),或作为对象的字段。分配后,它们将使用 Initobj 进行初始化。但是, newobj 指令可用于在堆栈上创建值类型的新实例,然后可以将其作为参数传递,存储在本地等。
For each value type which is numeric, from byteto double, has a defined op-code. Although they are declared as struct, there's some difference in the generated IL as shown.
对于每个数值类型,从byte到double,都有一个定义的操作码。尽管它们被声明为struct,但在生成的 IL 中存在一些差异,如图所示。
Here are two more things to mention:
这里还有两件事要说:
ValueTypeitself is declared a abstract classThat is, you cannot newit directly.
structs cannot contain explicit parameterless constructorsThat is, when you newa
struct, you would fall into the case above of eitherNewMyLongorNewMyLongWithValue.
ValueType本身被声明为抽象类也就是说,您不能直接对其进行new。
structs 不能包含显式无参数构造函数也就是说,当您新建a 时
struct,您将陷入上述NewMyLongor的情况NewMyLongWithValue。
To summarize, newfor the value types and structures are for the consistency of the language concept.
总之,新的值类型和结构是为了语言概念的一致性。
回答by nawfal
Catch Eric Lippert's excellent answer from this thread.To quote him:
抓住埃里克利珀的从这个线程出色答卷。引用他的话:
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.)
当您“新建”一个值类型时,会发生三件事。首先,内存管理器从短期存储中分配空间。其次,构造函数被传递一个对短期存储位置的引用。构造函数运行后,短期存储位置中的值将复制到该值的存储位置,无论该位置发生在何处。请记住,值类型的变量存储实际值。
(请注意,如果编译器可以确定这样做永远不会将部分构造的结构暴露给用户代码,则允许编译器将这三个步骤优化为一个步骤。也就是说,编译器可以生成代码,只需将引用传递给最终存储位置到构造函数,从而节省一份分配和一份副本。)
(Making this answer since it really is one)
(做出这个答案,因为它确实是一个)
回答by Owen Reynolds
In a struct, the newkeyword is needlessly confusing. It doesn't do anything. It's just required if you want to use the constructor. It does notperform a new.
在结构体中,new关键字是不必要的混淆。它什么也不做。如果您想使用构造函数,它只是必需的。它并没有执行new。
The usual meaning of newis to allocate permanent storage (on the heap.)
A language like C++ allows new myObject()or just myObject(). Both call the same constructor. But the former creates a new object and returns a pointer. The latter merely creates a temp. Any struct or class can use either. newis a choice, and it means something.
的通常含义new是分配永久存储(在堆上)。像 C++ 这样的语言允许new myObject()或只是myObject(). 两者都调用相同的构造函数。但是前者创建了一个新对象并返回一个指针。后者只是创造了一个温度。任何结构或类都可以使用。new是一种选择,它意味着什么。
C# doesn't give you a choice. Classes are always in the heap, and structs are always on the stack. It isn't possible to perform a real newon a struct. Experienced C# programmers are used to this. When they see ms = new MyStruct();they know to ignore the newas just syntax. They know it's acting like ms = MyStruct(), which merely assigns to an existing object.
C# 没有给你选择。类总是在堆中,结构总是在栈上。不可能new在结构上执行实数。有经验的 C# 程序员已经习惯了这一点。当他们看到时,ms = new MyStruct();他们知道忽略newas just 语法。他们知道它的行为就像ms = MyStruct(),它只是分配给现有对象。
Oddly(?), classes require the new. c=myClass();isn't allowed (using the constructor to set values of existing object c.) You'd have to make something like c.init();. So you really never have a choice -- constructors always allocate for classes, and never for structs. The newis always just decoration.
奇怪的是(?),类需要new. c=myClass();不允许(使用构造函数设置现有对象的值c。)您必须制作类似c.init();. 所以你真的永远没有选择——构造函数总是为类分配,而不是为结构分配。将new永远只是摆设。
I assume the reason for requiring fake new's in structs is so you can easily change a struct into a class (assuming you always use myStruct=new myStruct();when you first declare, which is recommended.)
我认为new在结构中需要 fake 的原因是您可以轻松地将结构更改为类(假设您myStruct=new myStruct();在首次声明时始终使用,这是推荐的。)

