2009-04-01 6 views
10

Có thể truyền phương thức của giao diện dưới dạng tham số không?Truyền phương thức của Giao diện làm tham số

Tôi đang cố gắng một cái gì đó như thế này:

interface 

type 
    TMoveProc = procedure of object; 
    // also tested with TMoveProc = procedure; 
    // procedure of interface is not working ;) 

    ISomeInterface = interface 
    procedure Pred; 
    procedure Next; 
    end; 

    TSomeObject = class(TObject) 
    public 
    procedure Move(MoveProc: TMoveProc); 
    end; 

implementation 

procedure TSomeObject.Move(MoveProc: TMoveProc); 
begin 
    while True do 
    begin 
    // Some common code that works for both procedures 
    MoveProc; 
    // More code... 
    end; 
end; 

procedure Usage; 
var 
    o: TSomeObject; 
    i: ISomeInterface; 
begin 
    o := TSomeObject.Create; 
    i := GetSomeInterface; 
    o.Move(i.Next); 
    // somewhere else: o.Move(i.Prev); 
    // tested with o.Move(@i.Next), @@... with no luck 
    o.Free; 
end; 

Nhưng nó không làm việc vì:

E2010 loại không tương thích: 'TMoveProc' và 'thủ tục, con trỏ không định kiểu hoặc tham số không kiểu'

Tất nhiên tôi có thể thực hiện phương pháp riêng cho từng cuộc gọi, nhưng điều đó thật xấu. Có cách nào tốt hơn không?

Delphi 2006


Edit: tôi biết rằng tôi có thể vượt qua toàn bộ giao diện, nhưng sau đó tôi phải xác định các chức năng sử dụng. Tôi không muốn hai thủ tục giống hệt nhau với một cuộc gọi khác nhau.

Tôi có thể sử dụng thông số thứ hai, nhưng điều đó cũng quá xấu.

type 
    SomeInterfaceMethod = (siPred, siNext) 

procedure Move(SomeInt: ISomeInterface; Direction: SomeInterfaceMethod) 
begin 
    case Direction of: 
    siPred: SomeInt.Pred; 
    siNext: SomeInt.Next 
    end; 
end; 

Cảm ơn tất cả sự giúp đỡ và ý tưởng. Giải pháp sạch (cho Delphi 2006) của tôi là Khách truy cập của Diego. Bây giờ tôi đang sử dụng đơn giản ("xấu xí") wrapper (của riêng tôi, cùng một giải pháp của TOndrej và Aikislave).

Nhưng đúng Câu trả lời là "không có (trực tiếp) cách để vượt qua các phương pháp giao diện như thông số mà không một số loại của nhà cung cấp.

+0

Các mã trong TSomeObject.Move trông giống như một trường hợp sử dụng cho mẫu 'Chiến lược'. MoveProc có thể là một phương thức của một lớp TAbstractMoveProcStrategy, nơi các lớp con thực hiện hành vi được yêu cầu trong phương thức Move. TMovePredStrategy/TMoveNextStrategy sẽ có các procs Move khác nhau. – mjn

Trả lời

1

Dưới đây là một giải pháp mà đang làm việc trong Delphi 20006. Nó tương tự như ý tưởng của @Rafael, nhưng sử dụng giao diện:

interface 

type 
    ISomeInterface = interface 
    //... 
    end; 

    IMoveProc = interface 
    procedure Move; 
    end; 

    IMoveProcPred = interface(IMoveProc) 
    ['{4A9A14DD-ED01-4903-B625-67C36692E158}'] 
    end; 

    IMoveProcNext = interface(IMoveProc) 
    ['{D9FDDFF9-E74E-4F33-9CB7-401C51E7FF1F}'] 
    end; 


    TSomeObject = class(TObject) 
    public 
    procedure Move(MoveProc: IMoveProc); 
    end; 

    TImplementation = class(TInterfacedObject, 
     ISomeInterface, IMoveProcNext, IMoveProcPred) 
    procedure IMoveProcNext.Move = Next; 
    procedure IMoveProcPred.Move = Pred; 
    procedure Pred; 
    procedure Next; 
    end; 

implementation 

procedure TSomeObject.Move(MoveProc: IMoveProc); 
begin 
    while True do 
    begin 
    // Some common code that works for both procedures 
    MoveProc.Move; 
    // More code... 
    end; 
end; 

procedure Usage; 
var 
    o: TSomeObject; 
    i: ISomeInterface; 
begin 
    o := TSomeObject.Create; 
    i := TImplementation.Create; 
    o.Move(i as IMoveProcPred); 
    // somewhere else: o.Move(i as IMoveProcNext); 
    o.Free; 
end; 
+0

Điều này rất hứa hẹn – DiGi

1

Bạn có thể không. Bởi vì sự Phạm vi của giao diện nó sẽ có thể (có lẽ?) cho Giao diện được phát hành trước khi bạn gọi hàm .Next. Nếu bạn muốn thực hiện điều này, bạn nên chuyển toàn bộ giao diện đến phương thức của bạn thay vì chỉ là phương thức

Đã chỉnh sửa ... Xin lỗi, bit tiếp theo này , cụ thể là "Giao diện" bit có nghĩa là trong jest.

Ngoài ra, và tôi có thể sai Ở đây, i.Next không phải là một phương pháp của đối tượng, theo loại def của bạn, nó sẽ là một phương pháp giao diện!

Xác định lại chức năng của bạn

TSomeObject = class(TObject) 
    public 
     procedure Move(Const AMoveIntf: ISomeInterface); 
    end; 

    Procedure TSomeObject.Move(Const AMoveIntf : ISomeInterface); 
    Begin 
     ....; 
     AMoveIntf.Next; 
    end; 

    O.Move(I); 

Hope this helps.

+0

Không, nó không hoạt động - giao diện ẩn đối tượng :( – DiGi

+0

Thủ tục Move (Const AInterface: ISomeInterface); Begin .... AInterface.Next; End; – Aikislave

4

Tôi không biết lý do chính xác tại sao bạn cần làm điều đó, nhưng, cá nhân, tôi nghĩ sẽ tốt hơn khi chuyển toàn bộ đối tượng "Mover" thay vì một trong các phương pháp của nó. Tôi đã sử dụng cách tiếp cận này trong quá khứ, nó được gọi là mẫu "Khách truy cập". tiOPF, một khuôn khổ kiên trì đối tượng, sử dụng nó rộng rãi và cung cấp cho bạn một ví dụ tốt về cách hoạt động: The Visitor Pattern and the tiOPF.

Nó tương đối dài, nhưng nó tỏ ra rất hữu ích đối với tôi, ngay cả khi tôi không dùng tiOPF. Lưu ý bước 3 trong tài liệu, có tiêu đề "BướC# 3. Thay vì đi qua một con trỏ phương pháp, chúng tôi sẽ vượt qua một đối tượng".

DiGi, để trả lời nhận xét của bạn: Nếu bạn sử dụng mẫu Khách truy cập, thì bạn không có giao diện triển khai nhiều phương thức, nhưng chỉ một (Thực thi).Sau đó, bạn sẽ có một lớp cho mỗi hành động, như TPred, TNext, TSomething, và bạn chuyển một thể hiện của các lớp như vậy cho đối tượng được xử lý. Bằng cách này, bạn không cần phải biết phải gọi gì, bạn chỉ cần gọi "Visitor.Execute", và nó sẽ thực hiện công việc.

Ở đây bạn có thể tìm thấy một ví dụ cơ bản:

interface 

type 
TVisited = class; 

TVisitor = class 
    procedure Execute(Visited: TVisited); virtual; abstract; 
end; 

TNext = class(TVisitor) 
    procedure Execute (Visited: TVisited); override; 
end; 

TPred = class(TVisitor) 
    procedure Execute (Visited: TVisited); override; 
end; 

TVisited = class(TPersistent) 
public 
    procedure Iterate(pVisitor: TVisitor); virtual; 
end; 

implementation 

procedure TVisited.Iterate(pVisitor: TVisitor); 
begin 
    pVisitor.Execute(self); 
end; 

procedure TNext.Execute(Visited: TVisited); 
begin 
    // Implement action "NEXT" 
end; 

procedure TPred.Execute(Visited: TVisited); 
begin 
    // Implement action "PRED" 
end; 

procedure Usage; 
var 
    Visited: TVisited; 
    Visitor: TVisitor; 
begin 
    Visited := TVisited.Create; 
    Visitor := TNext.Create; 

    Visited.Iterate(Visitor); 
    Visited.Free; 
end; 
+0

Bởi vì Move chức năng không biết phương pháp nào nên gọi – DiGi

+0

Giải pháp sạch nhất – DiGi

-1

Bạn hiện đã TMoveProc định nghĩa là

TMoveProc = procedure of object; 

Hãy thử đưa ra khỏi các "đối tượng" mà ngụ ý một ẩn "này" con trỏ như đầu tiên tham số.

TMoveProc = procedure; 

Điều đó sẽ cho phép thực hiện thủ tục bình thường.

+0

Điều đó không làm việc quá .. Tôi sẽ cập nhật câu hỏi của tôi – DiGi

2

Làm thế nào về điều này:

type 
    TMoveProc = procedure(const SomeIntf: ISomeInterface); 

    TSomeObject = class 
    public 
    procedure Move(const SomeIntf: ISomeInterface; MoveProc: TMoveProc); 
    end; 

procedure TSomeObject.Move(const SomeIntf: ISomeInterface; MoveProc: TMoveProc); 
begin 
    MoveProc(SomeIntf); 
end; 

procedure MoveProcNext(const SomeIntf: ISomeInterface); 
begin 
    SomeIntf.Next; 
end; 

procedure MoveProcPred(const SomeIntf: ISomeInterface); 
begin 
    SomeIntf.Pred; 
end; 

procedure Usage; 
var 
    SomeObj: TSomeObject; 
    SomeIntf: ISomeInterface; 
begin 
    SomeIntf := GetSomeInterface; 
    SomeObj := TSomeObject.Create; 
    try 
    SomeObj.Move(SomeIntf, MoveProcNext); 
    SomeObj.Move(SomeIntf, MoveProcPred); 
    finally 
    SomeObj.Free; 
    end; 
end; 
+0

Đó là "Tất nhiên tôi có thể làm phương pháp riêng cho mỗi cuộc gọi, nhưng đó là xấu xí" ... – DiGi

6

Nếu bạn đang sử dụng Delphi 2009, bạn có thể làm điều này với một phương pháp vô danh:

TSomeObject = class(TObject) 
public 
    procedure Move(MoveProc: TProc); 
end; 

procedure Usage; 
var 
    o: TSomeObject; 
    i: ISomeInterface; 
begin 
    o := TSomeObject.Create; 
    i := GetSomeInterface; 
    o.Move(procedure() begin i.Next end); 

Vấn đề với cố gắng để vượt qua một tham chiếu đến chỉ là giao diện phương pháp là bạn không chuyển tham chiếu tới chính giao diện, do đó giao diện không thể được tính tham chiếu. Nhưng các phương thức nặc danh là chính chúng được tính tham chiếu, do đó, tham chiếu giao diện bên trong phương thức ẩn danh ở đây cũng có thể được tính tham chiếu. Đó là lý do tại sao phương thức này hoạt động.

+1

OK; phải sống với "xấu xí", sau đó (hoặc nâng cấp!), bởi vì phương pháp "không xấu xí" phá vỡ việc đếm tham chiếu sẽ trở nên tồi tệ hơn –

3

Mặc dù giải pháp lớp trình bao bọc hoạt động, tôi nghĩ đó là quá mức cần thiết. Đó là quá nhiều mã, và bạn phải tự quản lý tuổi thọ của đối tượng mới.

Có lẽ một giải pháp đơn giản là tạo ra phương pháp trong giao diện mà trả TMoveProc

ISomeInterface = interface 
    ... 
    function GetPredMeth: TMoveProc; 
    function GetNextMeth: TMoveProc; 
    ... 
end; 

Các lớp mà thực hiện giao diện có thể cung cấp procedure of object và nó sẽ có thể truy cập thông qua giao diện.

TImplementation = class(TInterfaceObject, ISomeInterface) 
    procedure Pred; 
    procedure Next; 

    function GetPredMeth: TMoveProc; 
    function GetNextMeth: TMoveProc; 
end; 

... 

function TImplementation.GetPredMeth: TMoveProc; 
begin 
    Result := Self.Pred; 
end; 

function TImplementation.GetNextMeth: TMoveProc; 
begin 
    Result := Self.Next; 
end; 
+0

+1 Điều này có tác dụng, nhưng người ta phải quan tâm, rằng tham chiếu đến việc thực hiện đối tượng vẫn tồn tại vào thời điểm đó, hàm được gọi . Đối với một giải pháp hoạt động tương tự nhưng sẽ xử lý các tham chiếu, hãy xem [answer của tôi] (http: // stackoverflow.com/a/24392902/2306907) – yonojoy