2008-11-01 29 views

Trả lời

11

Chỉ cần nghĩ mã callback điển hình khi bạn cần phải có dữ liệu có sẵn để gọi lại. Thông thường, dữ liệu này là cần thiết cho cuộc gọi lại chỉ, nhưng bạn phải nhảy qua một số vòng để có được nó ở đó mà không cần phải từ chức để thực hiện un-OOP thân thiện như biến toàn cục. Với các phương thức nặc danh, dữ liệu có thể ở nơi đó - bạn không cần phải mở rộng phạm vi của nó một cách không cần thiết hoặc sao chép nó vào một đối tượng trợ giúp nào đó. Chỉ cần viết mã gọi lại của bạn tại chỗ như một phương thức nặc danh và nó có thể truy cập và thao tác đầy đủ tất cả các biến cục bộ tại trang web nơi mà phương thức nặc danh được định nghĩa (không phải nơi nó được gọi!).

Có các khía cạnh khác của các phương pháp ẩn danh, rõ ràng nhất là thực tế là chúng, cũng: ẩn danh, nhưng đây là một thực tế khiến chúng "nhấp" cho tôi ...

+1

Đây là một bình luận khiến tôi trở thành 'AHA'. Không vui mừng 100% nhưng ít nhất tôi có thể thấy một thế giới thực sử dụng cho nó bây giờ. – Steve

1

Tôi đoán (Tôi không biết Delphi) điều này ngụ ý rằng bạn có thể tạo các chức năng như một loại đối tượng dữ liệu ngay bây giờ. Điều này có nghĩa là bạn có thể, ví dụ, chuyển các hàm thành các tham số cho các hàm khác. Ví dụ: Hàm sắp xếp có thể lấy hàm so sánh làm tham số, do đó linh hoạt hơn nhiều.

+1

một phần. Delphi đã có con trỏ hàm. Nhưng bây giờ họ có thể được tạo ra một cách vô cùng. Xem bình luận của tôi. –

15

Vui lòng xem closures.

Chức năng ẩn danh Delphi là các đóng.

Chúng được tạo trong các chức năng khác và do đó có quyền truy cập vào phạm vi chức năng đó. Điều này thậm chí như vậy nếu chức năng vô định được gán cho tham số hàm được gọi sau khi hàm ban đầu được gọi. (Tôi sẽ tạo một ví dụ trong giây lát).

type 
    TAnonFunc = reference to procedure; 
    TForm2 = class(TForm) 
    Memo1: TMemo; 
    Button1: TButton; 
    Button2: TButton; 
    Button3: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    procedure Button3Click(Sender: TObject); 
    private 
    F1 : TAnonFunc; 
    F2 : TAnonFunc; 
    end; 

procedure TForm2.Button1Click(Sender: TObject); 
var 
    a : Integer; 
begin 
    a := 1; 

    F1 := procedure 
    begin 
    a := a + 1; 
    end; 

    F2 := procedure 
    begin 
    Memo1.Lines.Add(IntToStr(a)); 
    end; 
end; 

Phương thức trên chỉ định hai chức năng ẩn danh cho trường F1 và F2. Đầu tiên tăng biến cục bộ và biến thứ hai hiển thị giá trị của biến.

procedure TForm2.Button2Click(Sender: TObject); 
begin 
    F1; 
end; 

procedure TForm2.Button3Click(Sender: TObject); 
begin 
    F2; 
end; 

Bây giờ bạn có thể gọi cả hai chức năng và chúng truy cập giống nhau. Vì vậy, gọi F1 hai lần và F2 một lần cho thấy một 3. Tất nhiên đây là một ví dụ đơn giản. Nhưng nó có thể được mở rộng sang mã hữu ích hơn.

Trong môi trường đa luồng, các chức năng ẩn danh có thể được sử dụng trong một cuộc gọi đến Đồng bộ hóa, giúp loại bỏ sự cần thiết của vô số phương pháp.

+1

ok, nhưng tôi vẫn không vui! Generics Tôi rất vui vì tôi có thể thấy 100 địa điểm tôi có thể sử dụng chúng. Phương pháp vô danh vẫn để tôi lạnh. – Steve

+1

Bạn không cần phải vui mừng, bạn sẽ tìm thấy một cách sử dụng cho chúng ;-). –

+0

Có lẽ vui mừng không phải là từ đúng, nhưng điều tôi ngụ ý là tôi không thể nghĩ ra một ví dụ thế giới thực (chưa), nơi tôi muốn nói "Vâng, đó chỉ là phương pháp Anaoymous là" – Steve

11

Có thể ví dụ này có thể được một số giá trị cho bạn. Ở đây tôi sẽ thực hiện một danh sách hiển thị có thể phóng to để vẽ trên một TCanvas mà không khai báo các loại lớp hiển thị khác nhau. Nó cũng làm cho việc sử dụng Generics nặng nề. Giả sử chúng ta có một TForm với một TPaintBox và một TTrackBar trên nó ...

type 
    TDisplayProc = TProc<TCanvas>; 

type 
    TFrmExample3 = class(TForm) 
    pbxMain: TPaintBox; 
    trkZoom: TTrackBar; 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    procedure pbxMainClick(Sender: TObject); 
    procedure pbxMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); 
    procedure pbxMainPaint(Sender: TObject); 
    procedure trkZoomChange(Sender: TObject); 
    private 
    FDisplayList: TList<TDisplayProc>; 
    FMouseX: Integer; 
    FMouseY: Integer; 
    FZoom: Extended; 
    procedure SetZoom(const Value: Extended); 
    protected 
    procedure CreateCircle(X, Y: Integer); 
    procedure CreateRectangle(X, Y: Integer); 
    function MakeRect(X, Y, R: Integer): TRect; 
    public 
    property Zoom: Extended read FZoom write SetZoom; 
    end; 

implementation 

{$R *.dfm} 

procedure TFrmExample3.PaintBox1Paint(Sender: TObject); 
var 
    displayProc: TDisplayProc; 
begin 
    for displayProc in FDisplayList do 
    displayProc((Sender as TPaintBox).Canvas); 
end; 

procedure TFrmExample3.CreateCircle(X, Y: Integer); 
begin 
    FDisplayList.Add(
    procedure (Canvas: TCanvas) 
    begin 
     Canvas.Brush.Color := clYellow; 
     Canvas.Ellipse(MakeRect(X, Y, 20)); 
    end 
); 
end; 

procedure TFrmExample3.CreateRectangle(X, Y: Integer); 
begin 
    FDisplayList.Add(
    procedure (Canvas: TCanvas) 
    begin 
     Canvas.Brush.Color := clBlue; 
     Canvas.FillRect(MakeRect(X, Y, 20)); 
    end 
); 
end; 

procedure TFrmExample3.FormCreate(Sender: TObject); 
begin 
    FDisplayList := TList<TDisplayProc>.Create; 
end; 

procedure TFrmExample3.FormDestroy(Sender: TObject); 
begin 
    FreeAndNil(FDisplayList); 
end; 

function TFrmExample3.MakeRect(X, Y, R: Integer): TRect; 
begin 
    Result := Rect(Round(Zoom*(X - R)), Round(Zoom*(Y - R)), Round(Zoom*(X + R)), Round(Zoom*(Y + R))); 
end; 

procedure TFrmExample3.pbxMainClick(Sender: TObject); 
begin 
    case Random(2) of 
    0: CreateRectangle(Round(FMouseX/Zoom), Round(FMouseY/Zoom)); 
    1: CreateCircle(Round(FMouseX/Zoom), Round(FMouseY/Zoom)); 
    end; 
    pbxMain.Invalidate; 
end; 

procedure TFrmExample3.pbxMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); 
begin 
    FMouseX := X; 
    FMouseY := Y; 
end; 

procedure TFrmExample4.SetZoom(const Value: Extended); 
begin 
    FZoom := Value; 
    trkZoom.Position := Round(2*(FZoom - 1)); 
end; 

procedure TFrmExample4.trkZoomChange(Sender: TObject); 
begin 
    Zoom := 0.5*(Sender as TTrackBar).Position + 1; 
    pbxMain.Invalidate; 
end; 
5

Mọi người đã cung cấp mã, vì vậy tôi sẽ liệt kê một số nơi chúng có thể hữu ích.

Giả sử bạn có một số mã GUI. Thông thường, đối với một cái gì đó giống như trình xử lý onclick của nút, bạn phải cung cấp một hàm sẽ được gọi khi nút đó được nhấp. Tuy nhiên, chúng ta hãy nói rằng tất cả các chức năng đó phải làm là một cái gì đó đơn giản như bật lên một hộp tin nhắn hoặc thiết lập một lĩnh vực một nơi nào đó. Giả sử bạn có hàng tá các nút này trong suốt mã của bạn. Nếu không có hàm ẩn danh, bạn sẽ phải có rất nhiều hàm gọi là "OnButton1Click", "OnExitButtonClick", v.v., có khả năng sẽ làm lộn xộn mã của bạn ... hoặc bạn có thể tạo các hàm ẩn danh ngay lập tức đính kèm với những sự kiện này và bạn không không phải lo lắng về họ nữa.

Sử dụng khác là lập trình chức năng. Giả sử bạn có danh sách các số. Bạn muốn lấy lại chỉ những con số chia hết cho ba.Có khả năng một hàm được gọi là filter, hàm này trả về một boolean và một danh sách và trả về một danh sách mới chỉ chứa các phần tử trong danh sách đầu tiên, khi được truyền cho hàm, trả về True. Ví dụ:

filter(isOdd, [1, 2, 3, 5, 6, 9, 10]) --> [1, 3, 5, 9] 

Nó muốn được gây phiền nhiễu để bị buộc phải định nghĩa một hàm "isDivisibleByThree", sau đó vượt qua nó để lọc, vì vậy khác sử dụng cho các chức năng ẩn danh ở đây sẽ được chỉ một cách nhanh chóng tạo ra một chức năng bạn sẽ không cần bất cứ nơi nào khác và vượt qua nó để lọc.

5

tôi trả lời câu hỏi của riêng tôi, nhưng tôi tìm thấy một lời giải thích tốt về phương pháp vô danh ở đây Can your programming language do this?

+0

+1 IMO Câu trả lời của bạn là câu trả lời hay nhất. – Sam

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