2010-02-16 28 views
7

Tôi có một câu hỏi đơn giản về đại biểu .net. Giả sử tôi có một cái gì đó như thế này:C# đại biểu, thời gian giải quyết tham chiếu

public void Invoke(Action<T> action) 
    { 
     Invoke(() => action(this.Value)); 
    } 

    public void Invoke(Action action) 
    { 
     m_TaskQueue.Enqueue(action); 
    } 

Hàm đầu tiên bao gồm tham chiếu đến this.Value. Trong suốt thời gian chạy, khi phương thức đầu tiên, với tham số chung được gọi, nó sẽ cung cấp this.Value bằng cách nào đó với phương thức thứ hai, nhưng làm thế nào? Những bước vào tâm trí tôi:

  • Gọi theo giá trị (struct) - giá trị hiện tại của this.Value được thông qua, vì vậy nếu m_TaskQueue thực hiện nó 5 phút sau, giá trị sẽ không được ở trạng thái gần đây của nó, nó sẽ là bất cứ điều gì khi tham khảo lần đầu.
  • Gọi bằng cách tham khảo (loại tài liệu tham khảo) - sau đó trạng thái gần đây nhất của Value sẽ được tham chiếu trong khi thực hiện hành động nhưng nếu tôi thay đổi this.Value để tham khảo khác trước khi thực hiện các hành động, nó vẫn sẽ được trỏ đến các tài liệu tham khảo cũ
  • Gọi theo tên (cả hai) - trong đó this.Value sẽ được đánh giá khi hành động được gọi. Tôi tin rằng việc thực hiện thực tế sẽ giữ một tham chiếu đến this sau đó đánh giá Value về điều đó trong quá trình thực thi thực tế của đại biểu vì không có cuộc gọi theo tên.

Tôi cho rằng đó sẽ là Gọi kiểu tên nhưng không thể tìm thấy bất kỳ tài liệu nào để tự hỏi liệu đó có phải là hành vi được xác định rõ hay không. Lớp học này là một cái gì đó giống như một diễn viên trong Scala hoặc Erlang vì vậy tôi cần nó được thread an toàn. Tôi không muốn chức năng Invoke để dereference Value ngay lập tức, sẽ được thực hiện trong một chủ đề an toàn cho this đối tượng theo m_TaskQueue.

Trả lời

18

Hãy để tôi trả lời câu hỏi của bạn bằng cách mô tả mã nào chúng tôi thực sự tạo cho điều này. Tôi sẽ đổi tên phương thức Invoke gây nhầm lẫn khác của bạn; nó không cần thiết để hiểu những gì đang xảy ra ở đây.

Giả sử bạn nói

class C<T> 
{ 
    public T Value; 
    public void Invoke(Action<T> action) 
    { 
     Frob(() => action(this.Value)); 
    } 
    public void Frob(Action action) 
    { // whatever 
    } 
} 

Trình biên dịch tạo ra mã như thể bạn đã thực sự viết:

class C<T> 
{ 
    public T Value; 

    private class CLOSURE 
    { 
    public Action<T> ACTION; 
    public C<T> THIS; 
    public void METHOD() 
    { 
     this.ACTION(this.THIS.Value); 
    } 
    } 

    public void Invoke(Action<T> action) 
    { 
     CLOSURE closure = new CLOSURE(); 
     closure.THIS = this; 
     closure.ACTION = action; 
     Frob(new Action(closure.METHOD)); 
    } 
    public void Frob(Action action) 
    { // whatever 
    } 
} 

Điều đó trả lời câu hỏi của bạn?

+0

Cảm ơn rất nhiều, đó là tinh thể rõ ràng ngay bây giờ :) –

7

Đại biểu lưu trữ tham chiếu đến biến, chứ không phải giá trị của biến đó. Nếu bạn muốn giữ giá trị hiện tại sau đó (giả sử nó là một kiểu giá trị), bạn cần phải thực hiện một bản sao cục bộ của nó:

public void Invoke(Action<T> action) 
{ 
    var localValue = this.Value; 
    Invoke(() => action(localValue)); 
} 

Nếu nó là một loại tài liệu tham khảo có thể thay đổi bạn có thể làm một bản sao local/bản sao sâu .

+1

Bắt buộc Eric Lippert plug: http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx –

3

Điều quan trọng là phải nhớ phạm vi đó là từ vựng; đó là một cái gì đó trình biên dịch sẽ chăm sóc. Vì vậy, nó nắm bắt các biến số , không phải là các giá trị của chúng. Cho dù các giá trị đó là các loại giá trị hoặc loại tham chiếu thì hoàn toàn là một vấn đề khác.

Có lẽ một ví dụ hơi cực đoan hơn làm thay đổi hành vi của các đại biểu sẽ giúp:

var myVariable = "something"; 
Action a =() => Console.WriteLine(myVariable); 
myVariable = "something else entirely" 
a(); 

in "một cái gì đó khác hoàn toàn". Trong ánh sáng đó, nó không thực sự quan trọng bao nhiêu lần bạn quấn, lưu, hoặc di chuyển chức năng; nó vẫn đề cập đến biến nó kèm theo. Vì vậy, trong ngắn hạn, những gì quan trọng là giá trị của biến kèm theo khi các đại biểu thực sự thực hiện.

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