2013-08-05 26 views
16

xem xét mã C# sau:Làm thế nào để bắt hoặc cờ vấn đề tiềm ẩn do thứ tự khởi lĩnh vực tĩnh

using System; 
class Program 
{ 
    static string string1 = "AAA"; 
    static string string2 = string1 + string3; 
    static string string3 = "BBB"; 

    static void Main() 
    { 
     Console.WriteLine(string2); 
    } 
} 

tôi đã viết một số mã như ngày nay trước đây và đã mong string2 để chứa các giá trị AAABBB, nhưng thay vào đó nó chỉ chứa AAA. Tôi đã làm một số đọc về thứ tự khởi tạo các biến tĩnh, nhưng nó có vẻ thích hợp với tôi rằng một số loại cảnh báo hoặc lỗi sẽ được tạo ra trong quá trình biên dịch.

Hai câu hỏi:

  1. Tại sao mã như vậy cho phép để biên dịch thành công? (và nếu câu trả lời là: "bởi vì đó là cách C# spec được viết", thì tại sao nó được viết theo cách đó? Có lý do nào tôi bỏ lỡ lý do tại sao điều này không phải lúc nào cũng chỉ là một lỗi biên dịch?)

  2. bất kỳ cách nào cách để nhận cảnh báo biên dịch hoặc một số loại cờ khác nếu tôi vô tình viết lại loại mã này trong tương lai không?

+4

_ * tôi nghĩ * không có cách nào thực sự dẫn đến "" AAA "' ... sao chép/dán, chạy ... oh wow nó! Tâm thổi O_O như tôi upvote và hy vọng Skeet hoặc Lippert sẽ trả lời này_ –

+0

Nếu đây là một vấn đề, không khởi tạo khai báo, nhưng thay vì chạy một constructor tĩnh, nơi bạn làm các bài tập. – spender

+5

ReSharper đưa ra cảnh báo cho việc này. – cadrell0

Trả lời

3

Là # 1: Lý do mã được phép biên dịch là vì, theo 10.4.5.1 của thông số C#, "trình khởi tạo biến trường tĩnh của một lớp tương ứng với chuỗi các bài tập được thực hiện theo thứ tự văn bản mà chúng xuất hiện trong khai báo lớp. " Điều đó có nghĩa là biến số string2 của bạn được khởi tạo rõ ràng thành "AAA" + null. Có thể cho rằng ít nhất bạn cũng nên cảnh báo ... về lý do tại sao IDE của bạn chọn không cảnh báo bạn, tôi không biết.

Liên quan đến # 2: Tôi không biết. Điều này có vẻ giống như một câu hỏi phù hợp hơn nếu được gắn thẻ với IDE của bạn.

+0

Thẻ được thêm cho VS2010 – RSW

1

Đó là bởi design.

Trường tĩnh initializers biến của một lớp tương ứng với một chuỗi các bài tập được thực hiện theo thứ tự văn bản trong mà chúng xuất hiện trong khai báo lớp.

C# có chính sách definite assignment có nghĩa là, ví dụ: trường được tự động khởi tạo. Do đó, string3 của bạn được tự động khởi tạo thành null và do đó, theo như trình biên dịch có liên quan, nó đã có giá trị.

Điều này có nghĩa là đầu ra của string2string1 + null (chỉ đơn giản là string2) và do đó không có lý do gì để trình biên dịch ném bất kỳ lỗi nào (mặc dù tôi thấy cảnh báo này hữu ích như thế nào).

+0

Nếu' string1', 'string2' và' string3' không tĩnh, trình biên dịch sẽ không cho phép mã này biên dịch chút nào. Nhưng vì chúng tĩnh, nó cho phép nó không có vấn đề gì cả. Nếu nó là do thiết kế, thì câu hỏi của tôi là "tại sao sự khác biệt"? – RSW

+0

Lý do lỗi biên dịch được ném trong trường hợp đó là bởi vì nó là bất hợp pháp đối với các biến mẫu tham chiếu đến 'this' (mà bạn đang ngầm làm http://msdn.microsoft.com/en-us/library/aa645759 % 28v = VS.71% 29.aspx). Bằng cách làm cho chúng không tĩnh, bạn đang nói 'this.string2 = this.string1 + this.string3'. – keyboardP

+0

Có, nhưng hãy suy nghĩ về lý do cơ bản đó là trường hợp. Và vì vậy tôi vẫn hỏi "tại sao sự khác biệt". Nếu giá trị của các biến mẫu có thể không được xác định tại thời điểm này, do đó làm cho các tham chiếu tới chúng không hợp lệ, tại sao trình biên dịch cho phép tham chiếu tới các trường tĩnh được sử dụng theo cách tương tự, * chỉ vì trình biên dịch xảy ra để khởi tạo các trường tĩnh thành mặc định giá trị sớm hơn so với trường không tĩnh? – RSW

6

Đối với câu hỏi 2:

Các công cụ như ReSharper bắt những điều này. Có thể có một thiết lập bên trong Visual Studio mà bạn có thể bật để có thêm đầu ra biên dịch chi tiết.

enter image description here

Dưới đây là đoạn code sau khi dọn dẹp Reshaper sản xuất "AAABBB"

class Program 
    { 
     private const string String1 = "AAA"; 
     private const string String2 = String1 + String3; 
     private const string String3 = "BBB"; 

     static void Main() 
     { 
      Console.WriteLine(String2); 
      Console.ReadLine(); 

     } 

    } 

Là một mặt lưu ý, tôi muốn nói vì chúng ta thường đọc từ trên xuống dưới nó có vẻ hợp lý cho khởi tạo xảy ra theo cách tương tự như được mô tả trong 10.4.5.1 của đặc tả C#.

+0

Tôi muốn biết liệu Visual Studio có thực sự có cài đặt để bắt/gắn cờ hay không điều này, vì chúng tôi không có R # nơi tôi làm việc. – RSW

+0

Tôi đồng ý với "tính hợp lý" của việc khởi tạo từ trên xuống dưới. Tuy nhiên, nếu các trường này không tĩnh, trình biên dịch sẽ có lỗi. * Điều đó * có vẻ hợp lý hơn với tôi. – RSW

+0

@RSW - Đúng, nhưng để có được lý do đó một người từ đội ngũ thiết kế C# sẽ phải trả lời. :) –

6

Nếu bạn nghĩ về các trình khởi tạo tĩnh của bạn như một phần logic của hàm tạo tĩnh, mọi thứ sẽ có ý nghĩa hơn rất nhiều. Trong trường hợp của bạn, nó giống như bạn đã viết:

private static string String1; 
private static string String2; 
private static string String3; 

static Program() 
{ 
    String1 = "AAA"; 
    String2 = String1 + String3; 
    String3 = "BBB"; 
} 

Lý do khởi tạo tĩnh không hoạt động theo cách bạn muốn là không thể trong trường hợp chung cho trình biên dịch sắp xếp lại thứ. Nó có thể trong trường hợp này, và trong nhiều trường hợp khác. Nhưng nếu bạn xem xét các hiệu ứng bậc 2 như tôi đã đề cập trong answer to a similar question, trình biên dịch không thể sắp xếp lại bất cứ thứ gì một cách đáng tin cậy.

Nó sẽ gây nhầm lẫn trong cực đối với trình biên dịch để làm điều đó sắp xếp lại "đôi khi".

+0

Cảm ơn câu trả lời. Nó cung cấp thông tin chi tiết về cách * mọi thứ đang hoạt động. Tuy nhiên, điều tôi tò mò là * tại sao trình biên dịch lại cho phép một kịch bản như vậy bị trượt. Tại sao nó ít nhất là cờ này với một cảnh báo? Có trường hợp sử dụng nào yêu cầu loại tính linh hoạt này, sao cho trình biên dịch vui vẻ cho phép chúng bị trượt không? Dường như với tôi rằng kể từ khi sử dụng như vậy là mạnh mẽ khuyến khích bởi spec C#, hầu hết sử dụng loại hành vi này trong tự nhiên là vô tình, như là trường hợp với tôi. – RSW

+0

@RSW: Eric Lippert, trước đây thuộc nhóm phát triển C#, đã viết blog một chút về những ưu và nhược điểm của cảnh báo. Tôi nghi ngờ nhóm biên dịch đã quyết định rằng điều này không đủ để giải quyết một cảnh báo. Xem http://ericlippert.com/2012/07/17/should-c-warn-on-null-dereference/ đối với trường hợp có liên quan phần nào. –

+0

Lippert cũng liên kết với [bài viết rất thú vị này] (http://blogs.msdn.com/b/ericlippert/archive/2011/03/03/danger-will-robinson.aspx) giải thích quy trình quyết định được sử dụng để quyết định nên tạo cảnh báo. Kịch bản mà tôi đề xuất làm cho nó vượt qua bốn tiêu chí đầu tiên, nhưng có thể thất bại trong hai tiêu chí cuối cùng, đó là: (5) "Công cụ khác có thể đưa ra cảnh báo không?", Trong trường hợp đó R # có thể, và (6) "Liệu người dùng có phát hiện lỗi ngay lập tức khi thử nghiệm không?", Điều này gây tranh cãi. Tôi sẽ được quan tâm để biết nếu đội C# thực sự xem xét điều này và từ chối nó. – RSW

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