2012-02-09 33 views
52

Trong C#, Structs được quản lý về mặt giá trị và các đối tượng được tham chiếu. Từ hiểu biết của tôi, khi tạo một thể hiện của một lớp, từ khóa new gây C# để sử dụng thông tin lớp để làm cho ví dụ, như trong dưới đây:Từ khóa "mới" làm gì với cấu trúc trong C#?

class MyClass 
{ 
    ... 
} 
MyClass mc = new MyClass(); 

Đối với struct, bạn không tạo ra một đối tượng mà chỉ đơn giản thiết lập một biến với một giá trị:

struct MyStruct 
{ 
    public string name; 
} 
MyStruct ms; 
//MyStruct ms = new MyStruct();  
ms.name = "donkey"; 

những gì tôi không hiểu là nếu khai báo các biến bởi MyStruct ms = new MyStruct(), những gì đang từ khóa new đây đang làm để báo cáo kết quả? . Nếu struct không thể là một đối tượng, thì new ở đây sẽ khởi tạo ở đâu?

+2

Một thể hiện của một 'struct' * là * một đối tượng. Sự khác biệt bạn có lẽ là sự hiểu lầm là giữa các loại giá trị và các loại tham chiếu. –

+0

nhưng trong C không có đối tượng và cấu trúc không phải là một đối tượng. Vì vậy, trong C# struct được thực hiện như là đối tượng? – KMC

+1

Suy nghĩ của C# về C là không hữu ích. Bỏ qua những khác biệt về cú pháp, chúng là những ngôn ngữ hoàn toàn khác nhau. –

Trả lời

43

Từ struct (C# Reference) trên 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.

Để hiểu biết của tôi, bạn sẽ không thực sự có thể sử dụng một cấu trúc đúng cách mà không sử dụng mới trừ khi bạn chắc chắn rằng bạn khởi tất cả các lĩnh vực bằng tay. Nếu bạn sử dụng toán tử mới, hàm tạo sẽ làm điều này cho bạn.

Hy vọng xóa nó đi. Nếu bạn cần làm rõ về điều này cho tôi biết.


Sửa

Có khá a thread bình luận dài, vì vậy tôi nghĩ rằng tôi muốn bổ sung thêm một chút ở đây. Tôi nghĩ cách tốt nhất để hiểu nó là để cho nó đi. Tạo một dự án bàn điều khiển trong Visual Studio có tên là "StructTest" và sao chép đoạn mã sau vào nó.

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

Chơi xung quanh với nó. Loại bỏ các nhà thầu và xem điều gì xảy ra. Hãy thử sử dụng một constructor mà chỉ khởi tạo một biến (tôi đã nhận xét một biến ... nó sẽ không biên dịch). Hãy thử và không có từ khóa mới (Tôi đã nhận xét một số ví dụ, bỏ ghi chú và thử dùng).

+0

"cấu trúc có thể được khởi tạo"? nhưng struct không thể là một đối tượng, phải không? Là các "trường" trong cấu trúc các thuộc tính và phương thức - nếu struct không phải là một đối tượng tại sao các trường của nó cần được khởi tạo? Tôi nghĩ tôi cần làm rõ hơn. cảm ơn. – KMC

+0

Tại sao * sẽ không * các trường của struct cần được khởi tạo, nếu bạn không gọi một hàm tạo? Nếu bạn không khởi tạo chúng, và bạn không gọi một hàm khởi tạo để khởi tạo chúng, chúng vẫn chưa được khởi tạo. – hvd

+0

Một cấu trúc là một đối tượng của các loại. Được mô tả trên trang web của Microsoft dưới dạng "đối tượng nhẹ". Nó có thể có các biến, nhưng không có chức năng. Cấu trúc giống như một lớp, nhưng tất cả các thành viên đều công khai và bạn không thể có bất kỳ chức năng nào. Nó cho phép bạn lưu trữ thông tin, nhưng bạn không thể thao tác hoặc kiểm soát thông tin như bạn có thể trong một lớp học. Bạn có thể tạo cấu trúc "mới" để sử dụng cùng một biến nhưng xóa tất cả dữ liệu. – joshhendo

3

Sử dụng "MyStuct mới()" đảm bảo rằng tất cả các trường được đặt thành một số giá trị. Trong trường hợp trên, không có gì khác biệt. Nếu thay vì đặt ms.name cho bạn khi cố gắng đọc nó, bạn sẽ nhận được lỗi "Sử dụng trường có thể chưa được gán" tên "" trong VS.

3

Bất cứ khi nào một đối tượng hoặc cấu trúc xuất hiện, tất cả các trường của nó đều tồn tại; nếu bất kỳ trường nào trong số đó là các kiểu cấu trúc, thì tất cả các trường lồng nhau đều tồn tại. Khi một mảng được tạo ra, tất cả các phần tử của nó đi vào sự tồn tại (và, như trên, nếu bất kỳ phần tử nào trong số đó là các cấu trúc, thì các trường của các cấu trúc đó cũng đi vào sự tồn tại). Tất cả điều này xảy ra trước khi bất kỳ mã trình xây dựng nào có cơ hội chạy.

Trong .net, một constructor struct là một cách hiệu quả gì hơn là một phương pháp mà phải mất một cấu trúc như một tham số 'out'. Trong C#, một biểu thức gọi một hàm tạo cấu trúc sẽ phân bổ một cá thể cấu trúc tạm thời, gọi hàm khởi tạo trên đó và sau đó sử dụng cá thể tạm thời đó làm giá trị của biểu thức. Lưu ý rằng điều này khác với vb.net, trong đó mã được tạo cho một hàm khởi tạo sẽ bắt đầu bằng cách loại bỏ tất cả các trường, nhưng nơi mã từ người gọi sẽ cố gắng để hàm tạo hoạt động trực tiếp trên đích. Ví dụ: myStruct = new myStructType(whatever) trong vb.net sẽ xóa myStruct trước câu lệnh đầu tiên của hàm tạo thi hành; bên trong hàm tạo, mọi ghi vào đối tượng đang xây dựng sẽ ngay lập tức hoạt động theo số myStruct.

0

ValueType và cấu trúc là thứ đặc biệt trong C#. Tại đây tôi sẽ cho bạn biết điều gì xảy ra khi bạn mới điều gì đó.

Dưới đây, chúng tôi đã sau

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

Và IL tạo của các phương pháp của lớp thử nghiệm sẽ là

  • 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 
    } 
    

Hành vi của các phương pháp được nhận xét trong mã IL. Và bạn có thể muốn xem qua số OpCodes.InitobjOpCodes.Newobj. Loại giá trị thường được khởi tạo với OpCodes.Initobj, nhưng như MSDN nói OpCodes.Newobj cũng sẽ được sử dụng.

  • mô tả trong OpCodes.Newobj

    Value types are not usually created 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.

Đối với mỗi loại giá trị đó là số, byte-double, có một op-code xác định. Mặc dù chúng được khai báo là struct, có một số khác biệt trong IL được tạo ra như được hiển thị.

Dưới đây là hai điều cần đề cập đến:

  1. ValueType tự được khai báo một lớp trừu tượng

    Đó là, bạn không thể mới nó trực tiếp.

  2. struct s không thể chứa constructor parameterless rõ ràng

    Đó là, khi bạn mới một struct, bạn sẽ rơi vào trường hợp trên của một trong hai NewMyLong hoặc NewMyLongWithValue.

Để tóm tắt, mới cho các loại giá trị và cấu trúc là dành cho tính nhất quán của các khái niệm ngôn ngữ.

12

Catch Eric Lippert's excellent answer from this thread. Để báo ông:

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.)

(Làm câu trả lời này vì nó thực sự là một)

+0

Cần lưu ý rằng vì các tham số 'out' là một khái niệm C#, thay vì được sử dụng bởi .NET Runtime, truyền một cấu trúc được xây dựng một phần như tham số' out' sang phương thức bên ngoài sẽ hiển thị các giá trị của nó tới mã bên ngoài, mặc dù trình biên dịch C# sẽ giả sử nó sẽ không. Ví dụ, người ta có thể định nghĩa một cấu trúc theo cách mà 'myThing = newmyThing (5);' sẽ khởi tạo một trường 'myThing' trong khi để những người khác không bị ảnh hưởng. – supercat

+0

Nếu ai đó có thể hạ gục Eric, thì một số tuyên bố. Thực hiện tốt .. – nawfal

+2

Tôi nhận thấy rằng các nhóm ngôn ngữ khác nhau có thể có tầm nhìn riêng của họ về những gì NET nên được, và giả vờ rằng nó phù hợp với tầm nhìn của họ. Ví dụ, nhóm C# có thể chỉ ra rằng .NET * nên * có các tham số 'out' có thể thi hành, và nếu mọi người lập trình trong C#, nhưng một phương thức ảo với tham số' out' sẽ được các ngôn ngữ khác coi là một phương thức ảo với tham số 'ref'. Trong một số trường hợp, ngôn ngữ không bị giới hạn bởi tập hợp con tối thiểu các tính năng mà những người triển khai ngôn ngữ khác có thể muốn triển khai, nhưng cũng có những mối nguy hiểm. – supercat

-1

Trong một cấu trúc, các từ khóa new là không cần thiết khó hiểu. Nó không làm gì cả. Nó chỉ cần thiết nếu bạn muốn sử dụng hàm tạo. Nó không không thực hiện một new.

Ý nghĩa thông thường của new là cấp phát bộ nhớ vĩnh viễn (trên heap.) Ngôn ngữ như C++ cho phép new myObject() hoặc chỉ myObject(). Cả hai đều gọi cùng một hàm tạo. Nhưng trước đây tạo một đối tượng mới và trả về một con trỏ. Cái sau chỉ tạo ra một tạm thời. Bất kỳ cấu trúc hoặc lớp nào cũng có thể sử dụng. new là một lựa chọn, và nó có nghĩa là một cái gì đó.

C# không cung cấp cho bạn lựa chọn. Các lớp luôn ở trong heap, và các struct luôn ở trên stack. Không thể thực hiện new thực trên cấu trúc. C# lập trình viên giàu kinh nghiệm được sử dụng để điều này. Khi họ thấy ms = new MyStruct();, họ biết bỏ qua cú pháp chỉ là new. Họ biết nó hoạt động như ms = MyStruct(), mà chỉ đơn thuần gán cho một đối tượng hiện có.

Kỳ lạ (?), Các lớp học yêu cầu new. c=myClass(); không được phép (sử dụng hàm tạo để đặt giá trị của đối tượng hiện có c.) Bạn sẽ phải tạo một cái gì đó như c.init();. Vì vậy, bạn thực sự không bao giờ có một sự lựa chọn - các nhà xây dựng luôn phân bổ cho các lớp, và không bao giờ cho các cấu trúc. new luôn trang trí.

tôi giả lý do đòi hỏi giả new 's trong cấu trúc được vì vậy bạn có thể dễ dàng thay đổi một cấu trúc thành một lớp (giả sử bạn luôn luôn sử dụng myStruct=new myStruct(); khi bạn lần đầu tiên tuyên bố, được khuyến khích.)

+0

Suy nghĩ về chi tiết triển khai là sai, cấu trúc không phải lúc nào cũng được phân bổ trên ngăn xếp, ví dụ: struct các lĩnh vực trên một lớp học, boxing, vv ... và mới thực sự làm điều gì đó! –

+0

Sự nhầm lẫn là mọi người đang đọc câu hỏi này là "tại sao tôi nên sử dụng mới với cấu trúc". Nhưng nếu bạn đọc nó, câu hỏi thực sự là cụm từ cuối cùng "cái mới ở đây là gì?" Đó là một câu hỏi về lý do tại sao họ chọn cú pháp hài hước. –

Các vấn đề liên quan