2014-11-26 33 views
17
public struct Test 
{ 
    public double Val; 
    public Test(double val = double.NaN) { Val = val; } 
    public bool IsValid { get { return !double.IsNaN(Val); } } 
} 

Test myTest = new Test(); 
bool valid = myTest.IsValid; 

Ở trên cung cấp vì hàm tạo mặc định arg KHÔNG được gọi và đối tượng được tạo với giá trị mặc định val = 0.0.
Nếu cấu trúc là một lớp, hành vi là valid==false đó là những gì tôi mong đợi.Hành vi không trực quan với khởi tạo cấu trúc và đối số mặc định

Tôi thấy sự khác biệt này trong hành vi và đặc biệt là hành vi trong trường hợp cấu trúc đáng ngạc nhiên và không trực quan - điều gì đang xảy ra? Gì mặc định arg trên cấu trúc stuct phục vụ? Nếu vô ích của nó tại sao lại cho phép biên dịch này?

Cập nhật: Để làm rõ trọng tâm ở đây không phải là hành vi - mà là tại sao bản dịch này không biên dịch và hoạt động không liên quan. I.e Nếu mặc định arg không được áp dụng bởi vì trong trường hợp Test() mới, hàm tạo không được gọi là tại sao nó cho phép biên dịch?

+0

Tôi sẽ nói rằng bằng cách không chuyển một đối số thì bạn đang gọi hàm _implicit_ [default constructor] (http://msdn.microsoft.com/en-us/library/s1ax56ch.aspx) trên các cấu trúc đặt tất cả thành viên với giá trị mặc định của họ, thay vì gọi hàm tạo của bạn –

+0

@ DanielJ.G. Vâng, đó là trường hợp - nhưng quan điểm của tôi là không trực quan. Nó không phải về những gì đang xảy ra nhưng về lý do tại sao điều này biên dịch mà không cần cảnh báo. – Ricibob

+0

Có thể viết các phương thức/hàm tạo mà không có cách hợp lý để bạn gọi trực tiếp chúng (không giống như ở đây bạn có thể cung cấp tham số, và mặc định là "vô nghĩa") ngoại trừ phản xạ. Trình biên dịch C# sẽ phải cực kỳ phức tạp hơn (và có lẽ phải giải quyết vấn đề dừng) để * ngăn chặn * bạn biên dịch mã như vậy. Và bạn có thể * có ý định * rằng nó chỉ có thể sử dụng được thông qua sự phản chiếu, cho tất cả các trình biên dịch biết. –

Trả lời

8

Trong C# (ít nhất là cho đến khi C# 6 - see blog post), gọi new Test() tương đương với viết default(Test) - không có hàm tạo nào được gọi, giá trị mặc định được cung cấp.

Các arg mặc định phục vụ không có mục đích, những gì xảy ra là nó có khả năng kết quả của một sự giám sát trong việc thực hiện các trình biên dịch, do thực tế rằng đối số tùy chọn chỉ được thêm vào trong C# 4:

  • Mã kiểm tra các đối số tùy chọn không xung đột với các tình trạng quá tải hiện có là không biết về xung đột có thể xảy ra với bộ khởi tạo trong trường hợp các cấu trúc;
  • Mã dịch những gì new Test() có nghĩa là có lẽ không biết về sự tồn tại của các đối số tùy chọn;

    • Sau khi đào sâu vào ý kiến, tôi nhận thấy viên ngọc sau bởi Mads Torgersen:

      Đúng là việc thực hiện biên dịch cho đến nay "tối ưu hóa" 'mới T()' để có nghĩa là về cơ bản mặc định (T) khi T là một cấu trúc. Đó thực sự là một lỗi - nó luôn luôn được gọi là một nhà xây dựng không có tham số thực tế nếu có một - mà có thể có được tất cả cùng, vì nó được cho phép trong IL.

      Ví dụ của bạn, nó có nghĩa là new Test() được một cách hiệu quả thay thế bởi trình biên dịch để default(Test) - vì vậy đó là một lỗi, mà sẽ được cố định trong phiên bản kế tiếp của Visual Studio.

Nói cách khác, bạn có một trường hợp góc. Điều đó có lẽ sẽ là thời điểm tốt để xem xét cách ứng xử trong phiên bản tiếp theo của Visual Studio, vì hành vi đó đang thay đổi.

+0

Ngẫu nhiên, tình hình hơi tồi tệ hơn trong VB.NET, thiếu một kiểu gõ tương đương với 'default (T)' khác với 'new T()', như đã nói, không thực sự tương đương. – supercat

+0

Rõ ràng là không có gì thay đổi kể từ năm 2014. Tôi chạy mã @Ricibob trong ứng dụng giao diện điều khiển 1.1 lõi và kết quả vẫn là ** hợp lệ == true ** – user2340612

1

Tôi nghi ngờ điều này là do hàm tạo mặc định không có tham số không được phép trong C# vì vậy khi bạn gọi hàm khởi tạo Thử nghiệm không có tham số, nó chỉ khởi tạo chúng như bình thường. Kiểm tra bài này để biết thêm chi tiết (không hoàn toàn là một bản sao): Why can't I define a default constructor for a struct in .NET?

2

Đối với tất cả các loại giá trị T, new T()default(T) là tương đương. Họ không gọi bất kỳ nhà xây dựng nào, họ chỉ đơn thuần đặt tất cả các trường về 0. Đây cũng là lý do tại sao C# không cho phép bạn viết một hàm tạo tham số: public Test() { Val = double.NaN; } sẽ không biên dịch, vì sẽ không có cách nào cho hàm tạo đó được sử dụng.

Bạn đã tìm thấy trường hợp góc. Nhà xây dựng của bạn trông giống như được sử dụng cho new T(). Vì loại của bạn vẫn là một loại giá trị, nó không được sử dụng. Vì hàm tạo của bạn có thể được gọi là, không có lỗi nào được phát hành.

1

Vì cấu trúc không thể có một hàm tạo tham số do người dùng xác định.

Test(double val = double.NaN)trông như một, nhưng nó thực sự được biên dịch là Test(double val) với một số siêu dữ liệu về giá trị mặc định.

+0

Nhưng tại sao nhóm C# cho phép điều này thông qua hành vi này là trực quan. – Ricibob

2

Tôi thấy sự khác biệt này trong hành vi và đặc biệt là hành vi trong trường hợp cấu trúc đáng ngạc nhiên và không trực quan - điều gì đang xảy ra? Điều gì hiện mặc định arg trên cấu trúc stuct phục vụ? Nếu nó vô dụng tại sao cho phép biên dịch này?

Nó không phục vụ gì cả. Mã IL được phát ra sẽ không tạo ra một lời gọi tới hàm tạo với tham số mặc định, nhưng sẽ gọi default(Test). Dường như hoàn toàn hợp lý rằng trình biên dịch sẽ phát ra một cảnh báo nói rằng hàm tạo sẽ không được gọi ra (mặc dù đó là một chi tiết thực hiện). Tôi nộp một vấn đề trên http://connect.microsoft.com

Nếu chúng ta nhìn vào mã IL được tạo ra cho:

Test myTest = new Test(); 
bool valid = myTest.IsValid; 

Chúng tôi sẽ thấy:

IL_0000: ldloca.s 00 // myTest 
IL_0002: initobj  UserQuery.Test // default(Test); 
IL_0008: ldloca.s 00 // myTest 
IL_000A: call  UserQuery+Test.get_IsValid 

Lưu ý các cuộc gọi được thực hiện tại IL không phải là một phương pháp gọi hàm khởi tạo (trông giống như sau: call Test..ctor) , nó đã tạo một cuộc gọi tới initobj:

Khởi tạo từng trường của loại giá trị tại địa chỉ được chỉ định thành tham chiếu rỗng hoặc 0 của loại nguyên thủy phù hợp. Không giống như Newobj, initobj không gọi phương thức hàm tạo. Initobj được thiết kế để khởi tạo các loại giá trị, trong khi newobj được sử dụng để cấp phát và khởi tạo các đối tượng.

Điều đó có nghĩa trình biên dịch chỉ đơn thuần là bỏ qua hàm tạo với các tham số mặc định, cho đến khi C# -6.0 bị cấm khai báo một hàm tạo như vậy.

@JonSkeet mất này để độ sâu lớn trong câu trả lời của mình để Does using "new" on a struct allocate it on the heap or stack?

Sửa:

Tôi thực sự hỏi Mads Torgerson một câu hỏi liên quan đến việc sử dụng mới của các nhà xây dựng parameterless trong C# -6.0 mà tôi nghĩ có liên quan, và ông nói:

@Yuval và những người khác, liên quan đến nhà xây dựng parameterless trên cấu trúc: những điều cần nhận thức được rằng, trước và bây giờ, nhà xây dựng không nhất thiết chạy trên cấu trúc . Tất cả những gì chúng tôi đã làm là thêm khả năng có một hàm tạo tham số không được đảm bảo để chạy. Có là không có cách hợp lý để có các cấu trúc được đảm bảo là đã được khởi tạo và không có tham số không hỗ trợ điều đó.

Những điều mà các nhà xây dựng không có tham số giúp đỡ là cho phép bạn có một hàm tạo không tham số.

Tôi nghĩ rằng một nguồn chính gây nhầm lẫn là 'S mới()' được phép có nghĩa là 'mặc định (S)'. Đó là một sai lầm lịch sử trong ngôn ngữ và tôi mong muốn tôi có thể mang nó đi. Tôi sẽ không khuyến khích bất kỳ ai sử dụng 'mới S()' trên cấu trúc không có tham số hàm tạo. Theo như tôi có thể nói, điều này là bởi vì cú pháp mặc định (S) không tồn tại trong C# 1.0, vì vậy đây chỉ là cú pháp được sử dụng cho nhận giá trị mặc định của một cấu trúc.

Đúng là việc triển khai trình biên dịch cho đến nay "tối ưu" 'T mới()' có nghĩa cơ bản là mặc định (T) khi T là cấu trúc. Đó là thực sự là một lỗi - nó luôn luôn được gọi là một thực tế constructor parameterless nếu có một - mà có thể có được tất cả cùng, vì nó được cho phép trong IL. Chúng tôi đang sửa lỗi này, để chúng tôi sẽ gọi hàm tạo ngay cả trong trường hợp chung.

Ngữ nghĩa do đó là sạch: S mới() là cách duy nhất để chạy một constructor parameterless trên một cấu trúc, và nó luôn chạy mà constructor - thậm chí thông qua Generics.

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