2011-01-18 16 views
5

Estoy serializando y deserializando un objeto (descendiente TComponent) usando el ejemplo en la sección ComponentToString en el archivo de ayuda de Delphi. Esto es para poder almacenar el objeto en un campo VARCHAR en la base de datos.¿Puedo hacer un constructor que deserialice una versión de cadena de mi objeto?

Cuando necesito instanciar una nueva instancia de mi clase a partir de una cadena almacenada en la base de datos, ¿puedo hacer eso usando un constructor del formulario CreateFromString(AOwner: TComponent; AData: String)? ¿O debo usar un método que no sea de clase que devuelva una instancia de mi clase de componente?

Si puedo usar la versión del constructor, ¿cómo puedo "asignar" el valor de retorno de ReadComponent al "self" que está creando el constructor?

Aquí está el ejemplo deserialización del archivo de ayuda:

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; 

Respuesta

6

En general, sí, puede hacer que un constructor deserialice una cadena y utilice esa información para inicializar la nueva instancia. Un ejemplo trivial de eso sería una clase con un solo campo Integer. Pase una cadena al constructor y haga que el constructor llame al StrToInt e inicialice el campo con el resultado.

Pero si la única función que tiene para la deserialización es una que también crea la instancia, entonces no puede usar eso desde el constructor porque entonces terminará con dos instancias cuando solo quería una. No hay forma de que un constructor diga, "No importa, no construyas una instancia después de todo. Ya tengo uno en otro lugar".

Sin embargo, esa no es la situación en la que se encuentra.Como debe saber, TStream.ReadComponent le permite crear la instancia usted mismo. Solo instancia la clase si aún no le ha dado una instancia para usar. Usted debe ser capaz de escribir su constructor de la siguiente manera:

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; 

No estamos pasando el objeto actual, designado por Self, a ReadComponent. La transmisión ignorará el nombre de clase almacenado en la secuencia y supondrá que el objeto actual es de la clase correcta.

+2

¿Es posible que haya un pequeño error en su implementación? Creo que debería ser 'Create (nil)'; no 'heredado Create (nil);'. Con "heredado" se pierde la creación de cualquier almacenamiento de campo y las propiedades que introduce TLarryComponent. –

+1

Tienes razón. Estaba pensando que 'ReadComponent' se ocuparía de eso, pero ahora me doy cuenta de que no hay forma de que sea posible. –

+0

Ahora estoy trabajando perfectamente y proporcionando una solución elegante a mi problema. –

3

Utilice un class function estática en lugar de un 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; 

La parte AOwner podría ser un problema, sin embargo, ya que tiene TStream.ReadComponent sin parámetro para el dueño

Hay otra manera pregunta sobre ese problema:

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

Editar: He actualizado el ejemplo de código para incluir el propietario, también.

Tenga en cuenta que la inserción en la lista de componentes del propietario requiere un Name único o vacío para el componente que se está insertando.

+0

En realidad, el problema del propietario no es un problema. Estoy haciendo de este un descendiente TComponent solo para obtener acceso a las capacidades de transmisión automática. De hecho, no hay representación visible del objeto, no lo usaré en el momento del diseño, y administraré su tiempo de vida en código. Entonces puedo dejar al dueño nulo. –

3

Usted puede hacer esto mediante un método class (estática), pero no a través de un constructor.
Los compiladores de Delphis son llamados por el compilador intrínseco en la instancia recién creada, que ya está parcialmente inicializada (es de la clase deseada y el almacenamiento de instancia/campo es cero).

Si ve el origen de TStream.ReadComponent, encontrará que la clase real de los componentes se lee primero de la secuencia fuente, luego se construye una instancia vacía y se llena con RTTI de la ruta y se devuelve como resultado. Lo que significa:

Para usar TStream.ReadComponent, deberá registrar su clase en el sistema de transmisión de Delphis a través del RegisterClass.

+0

Gracias Viktor. Esta respuesta contiene una nota importante acerca de RegisterClass que probablemente me hubiera tenido rascándome la cabeza durante una hora si no hubiera pensado en señalarlo. –

Cuestiones relacionadas