2012-03-29 26 views
71

Tôi đang sử dụng Visual Studio 2010 + Resharper và nó cho thấy một cảnh báo về đoạn mã sau:phương pháp không tinh khiết được gọi là cho lĩnh vực readonly

if (rect.Contains(point)) 
{ 
    ... 
} 

rect là một lĩnh vực readonly Rectangle, và Resharper cho tôi cảnh báo này:

"Phương pháp không đúng được gọi cho trường giá trị chỉ đọc."

Phương pháp không tinh khiết là gì và tại sao cảnh báo này được hiển thị cho tôi?

+0

http://www.minddriven.de/index.php/technology/dot-net/code-contracts/code-contracts-method-purity – siride

Trả lời

81

Trước hết, câu trả lời của Jon, Michael và Jared về cơ bản là chính xác nhưng tôi có thêm một vài điều tôi muốn thêm vào chúng.

Phương pháp "không tinh khiết" có nghĩa là gì?

Việc mô tả các phương pháp thuần túy dễ dàng hơn. Phương pháp "thuần túy" có các đặc điểm sau:

  • Đầu ra của nó hoàn toàn được xác định bởi đầu vào; đầu ra của nó không phụ thuộc vào ngoại vi như thời gian trong ngày hoặc các bit trên đĩa cứng của bạn. Sản lượng của nó không phụ thuộc vào lịch sử của nó; gọi phương thức với một đối số đã cho hai lần sẽ cho kết quả tương tự.
  • Phương pháp thuần túy không tạo ra đột biến quan sát được trên thế giới xung quanh nó. Một phương pháp thuần túy có thể chọn để biến đổi trạng thái riêng tư vì lợi ích của hiệu quả, nhưng một phương thức thuần túy không làm thay đổi một trường của đối số của nó.

Ví dụ: Math.Cos là phương pháp thuần túy. Đầu ra của nó chỉ phụ thuộc vào đầu vào của nó, và đầu vào không bị thay đổi bởi cuộc gọi.

Phương pháp không tinh khiết là phương pháp không thuần khiết.

Một số mối nguy hiểm khi truyền các cấu trúc chỉ đọc để làm hỏng các phương pháp là gì?

Có hai điều cần lưu ý. Việc đầu tiên là một trong những chỉ ra bởi Jon, Michael và Jared, và đây là một trong những Resharper là cảnh báo bạn về. Khi bạn gọi một phương thức trên một cấu trúc, chúng tôi luôn chuyển một tham chiếu đến biến đó là bộ thu, trong trường hợp phương thức muốn biến đổi biến đó.

Vậy điều gì sẽ xảy ra nếu bạn gọi phương thức như vậy trên một giá trị chứ không phải là một biến? Trong trường hợp đó, chúng ta tạo một biến tạm thời, sao chép giá trị vào biến đó và chuyển một tham chiếu tới biến đó.

Biến chỉ đọc được coi là một giá trị, vì nó không thể bị đột biến bên ngoài hàm tạo. Vì vậy, chúng tôi đang sao chép biến này sang một biến khác và phương thức không tinh khiết có thể làm thay đổi bản sao, khi bạn dự định biến biến đó thành biến.

Đó là nguy cơ truyền một cấu trúc chỉ đọc như một máy thu . Ngoài ra còn có một nguy cơ đi qua một cấu trúc có chứa một trường chỉ đọc. Một cấu trúc có chứa một trường chỉ đọc là một thực tế phổ biến, nhưng về cơ bản nó viết một kiểm tra rằng hệ thống kiểu không có tiền để rút tiền mặt; "read-only-ness" của một biến cụ thể được xác định bởi chủ sở hữu của bộ nhớ. Một thể hiện của kiểu tham chiếu "sở hữu" bộ nhớ riêng của nó, nhưng một thể hiện của một kiểu giá trị thì không!

struct S 
{ 
    private readonly int x; 
    public S(int x) { this.x = x; } 
    public void Badness(ref S s) 
    { 
    Console.WriteLine(this.x); 
    s = new S(this.x + 1); 
    // This should be the same, right? 
    Console.WriteLine(this.x); 
    } 
} 

Một nghĩ rằng this.x sẽ không thay đổi vì x là một lĩnh vực readonly và Badness không phải là một nhà xây dựng. Nhưng ...

S s = new S(1); 
s.Badness(ref s); 

... thể hiện rõ sự sai lệch của điều đó. thiss tham chiếu đến cùng một biến và rằng biến số không chỉ đọc được!

+0

Khá hợp lý, nhưng vui lòng xem xét mã này: 'struct Id {' 'riêng readonly int _id; ' ' Id công cộng (int id) {_id = id; } ' ' public int ToInt() => _id; ' '} ' Tại sao * ToInt * không tinh khiết? – boskicthebrain

+0

@boskicthebrain: Câu hỏi của bạn có thực sự là "tại sao Resharper coi điều này là không tinh khiết?" Nếu đó là câu hỏi của bạn thì hãy tìm một người làm việc trên R # và hỏi họ! –

+0

Resharper sẽ đưa ra cảnh báo này ngay cả khi phương thức này bị vô hiệu và không làm gì ngoại trừ 'return'. Dựa trên đó, tôi đoán rằng các tiêu chí duy nhất là liệu phương thức có thuộc tính '[Pure]' hay không. – bornfromanegg

46

Phương pháp không tinh khiết là phương pháp không được đảm bảo để lại giá trị như cũ.

Trong .NET 4 bạn có thể trang trí các phương thức và loại với [Pure] để khai báo chúng là thuần túy và R # sẽ chú ý đến điều này. Thật không may, bạn không thể áp dụng nó cho các thành viên của người khác, và bạn không thể thuyết phục R # rằng một loại/thành viên là thuần túy trong một dự án .NET 3.5 theo như tôi biết. (Điều này cắn tôi trong Noda Time tất cả các thời gian.)

Các ý tưởng là nếu bạn đang gọi điện thoại một phương pháp mà đột biến một biến, nhưng bạn gọi nó vào một trường chỉ đọc, có lẽ không làm những gì bạn muốn, vì vậy R # sẽ cảnh báo bạn về điều này. Ví dụ:

public struct Nasty 
{ 
    public int value; 

    public void SetValue() 
    { 
     value = 10; 
    } 
} 

class Test 
{ 
    static readonly Nasty first; 
    static Nasty second; 

    static void Main() 
    { 
     first.SetValue(); 
     second.SetValue(); 
     Console.WriteLine(first.value); // 0 
     Console.WriteLine(second.value); // 10 
    } 
} 

Đây thực sự là cảnh báo hữu ích nếu mọi phương pháp thực sự thuần khiết được khai báo theo cách đó. Thật không may họ không, vì vậy có rất nhiều dương tính giả: (

+0

Điều đó có nghĩa là phương pháp không tinh khiết có thể thay đổi các trường cơ bản của biến thể valuetype truyền cho nó? – Acidic

+0

@Acidic: Không phải là giá trị đối số - ngay cả một phương pháp không tinh khiết có thể làm điều đó - nhưng giá trị bạn * gọi nó là *. (Xem ví dụ của tôi, nơi mà phương pháp thậm chí không có bất kỳ tham số nào.) –

+0

Điều đó có nghĩa là bất kỳ loại giá trị nào mà 'readonly' trở thành bất biến? – Acidic

5

Một phương pháp không tinh khiết là một phương pháp có thể có tác dụng phụ. Trong trường hợp này, Resharper dường như nghĩ rằng nó có thể thay đổi rect. t nhưng chuỗi các bằng chứng là bị hỏng.

11

nghe có vẻ như Reshaprer tin rằng phương pháp Contains có thể đột biến giá trị rect. Bởi vì rect là một readonly struct các biên dịch C# làm cho bản thủ của giá trị để ngăn chặn phương thức từ biến đổi một readonly Về cơ bản, mã cuối cùng trông giống như sau:

Rectangle temp = rect; 
if (temp.Contains(point)) { 
    ... 
} 

Trình chia sẻ lại cảnh báo bạn ở đây rằng Contains có thể biến đổi rect theo cách sẽ bị mất ngay lập tức bởi vì điều đó đã xảy ra tạm thời.

+0

Vì vậy, điều đó sẽ không ảnh hưởng đến bất kỳ logic được thực hiện trong phương pháp, chỉ ngăn chặn nó từ biến đổi giá trị mà nó đã được gọi, phải không? – Acidic

+1

@Acidic đúng là – JaredPar

13

Câu trả lời ngắn gọn là đây là xác thực sai và bạn có thể bỏ qua cảnh báo một cách an toàn.

Câu trả lời dài hơn là việc truy cập loại giá trị chỉ đọc sẽ tạo ra bản sao của nó, để bất kỳ thay đổi nào về giá trị được thực hiện bởi phương pháp sẽ chỉ ảnh hưởng đến bản sao. ReSharper không nhận ra rằng Contains là một phương pháp thuần túy (có nghĩa là nó không có tác dụng phụ). Eric Lippert nói về nó ở đây: Mutating Readonly Structs

+1

Vui lòng không bao giờ bỏ qua cảnh báo này cho đến khi được hiểu đầy đủ !!! Một ví dụ điển hình là điều này có thể đảm bảo an toàn cho bạn là cấu trúc này: "riêng tư chỉ đọc SpinLock _spinLock = new SpinLock();' - một khóa như vậy sẽ hoàn toàn vô dụng (vì công cụ sửa đổi chỉ đọc gây ra bản sao được tạo ra mỗi lần nhập phương pháp được gọi trên đó) – Jan

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