2011-01-18 40 views
5

Tôi đang tuần tự hóa và deserializing một đối tượng (TComponent hậu duệ) bằng cách sử dụng ví dụ trong phần ComponentToString trong tệp trợ giúp Delphi. Điều này là để tôi có thể lưu trữ đối tượng trong một trường VARCHAR trong cơ sở dữ liệu.Tôi có thể tạo một hàm tạo để deserializes một phiên bản chuỗi của đối tượng của tôi không?

Khi tôi cần khởi tạo phiên bản mới của lớp học từ một chuỗi được lưu trữ trong cơ sở dữ liệu, tôi có thể thực hiện điều đó bằng cách sử dụng hàm tạo của biểu mẫu CreateFromString(AOwner: TComponent; AData: String) không? Hoặc tôi có phải sử dụng một phương pháp không lớp trả về một thể hiện của lớp thành phần của tôi?

Nếu tôi có thể sử dụng phiên bản hàm tạo, làm cách nào để "ánh xạ" giá trị trả về của ReadComponent thành "tự" được tạo bởi hàm tạo?

Dưới đây là ví dụ deserialization từ tập tin giúp đỡ:

function StringToComponentProc(Value: string): TComponent; 
var 
    StrStream:TStringStream; 
    BinStream: TMemoryStream; 
begin 
    StrStream := TStringStream.Create(Value); 
    try 
    BinStream := TMemoryStream.Create; 
    try 
     ObjectTextToBinary(StrStream, BinStream); 
     BinStream.Seek(0, soFromBeginning); 
     Result:= BinStream.ReadComponent(nil); 
    finally 
     BinStream.Free; 
    end; 
    finally 
    StrStream.Free; 
    end; 
end; 

Trả lời

6

Nói chung, có, bạn có thể tạo một hàm tạo deserialize một chuỗi và sử dụng thông tin đó để khởi tạo phiên bản mới. Một ví dụ tầm thường về điều đó sẽ là một lớp với một trường đơn Integer. Vượt qua một chuỗi để các nhà xây dựng và có các nhà xây dựng gọi StrToInt và khởi tạo các lĩnh vực với kết quả. Nhưng nếu chức năng duy nhất bạn có để deserialization là một trong đó cũng tạo ra các thể hiện, sau đó bạn không thể sử dụng nó từ constructor bởi vì sau đó bạn sẽ kết thúc với hai trường hợp khi bạn chỉ muốn có một trong những ví dụ. Không có cách nào để một nhà xây dựng nói, "Đừng bận tâm; đừng xây dựng một ví dụ nào cả. Tôi đã có một nơi khác."

Tuy nhiên, đó không phải là tình huống bạn đang ở.Như bạn cần biết, TStream.ReadComponent cho phép bạn tự tạo cá thể. Nó chỉ khởi tạo lớp nếu bạn chưa cho nó một cá thể để sử dụng. Bạn sẽ có thể viết constructor của bạn như thế này:

constructor TLarryComponent.CreateFromString(const AData: string); 
var 
    StrStream, BinStream: TStream; 
begin 
    Create(nil); 
    StrStream := TStringStream.Create(AData); 
    try 
    BinStream := TMemoryStream.Create; 
    try 
     ObjectTextToBinary(StrStream, BinStream); 
     BinStream.Position := 0; 
     BinStream.ReadComponent(Self); 
    finally 
     BinStream.Free; 
    end; 
    finally 
    StrStream.Free; 
    end; 
end; 

Hiện chúng tôi đang đi qua các đối tượng hiện tại, do Self, để ReadComponent. Luồng sẽ bỏ qua tên lớp được lưu trữ trong luồng và giả định rằng đối tượng hiện tại là của lớp chính xác.

+2

Có thể có một lỗi nhỏ trong quá trình triển khai của bạn không? Tôi nghĩ rằng nó nên là 'Tạo (nil)'; không được thừa hưởng Create (nil); Với "kế thừa", bạn bỏ lỡ việc tạo bất kỳ lưu trữ trường và thuộc tính nào được giới thiệu bởi TLarryComponent. –

+1

Bạn nói đúng. Tôi đã nghĩ rằng 'ReadComponent' sẽ chăm sóc nó, nhưng tôi nhận ra bây giờ không có cách nào nó có thể. –

+0

Bây giờ làm việc hoàn hảo và cung cấp một giải pháp thanh lịch cho vấn đề của tôi. –

3

Sử dụng một tĩnh class function thay vì một constructor:

type 
    TYourClass = class(TComponent) 
    public 
    class function CreateFromString(AOwner: TComponent; AData: String): TYourClass; static; 
    end; 

implementation 

class function TYourClass.CreateFromString(AOwner: TComponent; AData: String): TYourClass; 
begin 
    Result := (StringToComponentProc(AData) as TYourClass); 
    if AOwner <> nil then 
    AOwner.InsertComponent(Result); 
end; 

Phần AOwner có thể là một vấn đề tuy nhiên, kể từ khi có TStream.ReadComponent không có tham số cho chủ sở hữu.

Có một vì vậy câu hỏi về vấn đề đó:

How can I specify the Owner of component read from a Delphi TStream?

Edit: Tôi đã cập nhật mẫu mã để bao gồm các chủ sở hữu, quá.

Lưu ý rằng việc chèn vào danh sách thành phần của chủ sở hữu yêu cầu Name độc đáo hoặc trống cho thành phần đang được chèn.

+0

Thực ra, vấn đề của chủ sở hữu không phải là vấn đề. Tôi đang làm cho một hậu duệ TComponent này chỉ để có được quyền truy cập vào khả năng tự động phát trực tuyến. Trong thực tế không có đại diện có thể nhìn thấy của đối tượng, tôi sẽ không được sử dụng nó tại thời gian thiết kế, và tôi sẽ quản lý tuổi thọ của nó trong mã. Vì vậy, tôi có thể rời khỏi chủ sở hữu nil. –

3

Bạn có thể làm điều này bằng phương pháp class (tĩnh), nhưng không phải qua constructor.
Các trình xây dựng của Delphis được gọi bởi trình biên dịch nội tại trên cá thể vừa tạo, vốn đã được khởi tạo một phần (nó là lớp mong muốn và lưu trữ cá thể/trường là zero-out).

Nếu bạn thấy nguồn của TStream.ReadComponent, bạn sẽ thấy rằng lớp thực của thành phần được đọc từ luồng nguồn lúc đầu, sau đó một phiên bản trống được xây dựng và điền bởi RTTI từ luồng và trả về kết quả. Điều này có nghĩa là:

Để sử dụng TStream.ReadComponent, bạn cần đăng ký lớp học của mình với hệ thống phát trực tuyến của Delphis qua RegisterClass.

+0

Cảm ơn Viktor. Câu trả lời này có chứa một lưu ý quan trọng về RegisterClass mà có lẽ sẽ khiến tôi gãi đầu trong một giờ nếu bạn không nghĩ đến nó. –

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