Pregunta

En Delphi, quiero ser capaz de crear un objeto privado que está asociado con una clase, y acceder a ella desde todas las instancias de esa clase.En Java, yo uso:

public class MyObject {
    private static final MySharedObject mySharedObjectInstance = new MySharedObject();
}

O, si MySharedObject necesitaba más complicado de inicialización, en Java me puede instanciar e inicializar en un inicializador estático bloque.

(Usted podría haber adivinado...Sé que mi Java pero soy bastante nuevo en Delphi...)

De todos modos, no quiero crear una instancia de una nueva MySharedObject cada vez que crear una instancia de MyObject, pero yo quiero un MySharedObject para ser accesibles desde cada instancia de MyObject.(En realidad es el registro que me ha impulsado a intentar averiguarlo - estoy usando Log4D y quiero guardar un TLogLogger como una variable de clase para cada clase que tiene la funcionalidad de registro.)

¿Cuál es el más bonito manera de hacer algo como esto en Delphi?

¿Fue útil?

Solución

Aquí es cómo voy a hacer que el uso de una variable de clase, una clase de procedimiento y un bloque de inicialización:

unit MyObject;

interface

type

TMyObject = class
   private
     class var FLogger : TLogLogger;
   public
     class procedure SetLogger(value:TLogLogger);
     class procedure FreeLogger;
   end;

implementation

class procedure TMyObject.SetLogger(value:TLogLogger);
begin
  // sanity checks here
  FLogger := Value;
end;

class procedure TMyObject.FreeLogger;
begin
  if assigned(FLogger) then 
    FLogger.Free;
end;

initialization
  TMyObject.SetLogger(TLogLogger.Create);
finalization
  TMyObject.FreeLogger;
end.

Otros consejos

 TMyObject = class
    private
      class var FLogger : TLogLogger;
      procedure SetLogger(value:TLogLogger);
      property Logger : TLogLogger read FLogger write SetLogger;
    end;

procedure TMyObject.SetLogger(value:TLogLogger);
begin
  // sanity checks here
  FLogger := Value;
end;

Tenga en cuenta que esta variable de clase será de escritura de cualquier instancia de la clase, por lo que usted puede configurar en algún otro lugar en el código, generalmente basadas en alguna condición (tipo de registrador etc.).

Editar:También será el mismo en todos los descendientes de la clase.Cambio en uno de los niños, y cambios para todos los descendientes de los casos.También se podría configurar la instancia predeterminada de la manipulación.

 TMyObject = class
    private
      class var FLogger : TLogLogger;
      procedure SetLogger(value:TLogLogger);
      function GetLogger:TLogLogger;
      property Logger : TLogLogger read GetLogger write SetLogger;
    end;

function TMyObject.GetLogger:TLogLogger;
begin
  if not Assigned(FLogger)
   then FLogger := TSomeLogLoggerClass.Create;
  Result := FLogger;
end;

procedure TMyObject.SetLogger(value:TLogLogger);
begin
  // sanity checks here
  FLogger := Value;
end;

El año pasado, Hallvard Vassbotn escribió en su blog acerca de un Delphi-hack yo había hecho para esto, se convirtió en un artículo de dos partes:

  1. Hack#17:Virtual de las variables de la clase, Parte I
  2. Hack#17:Virtual de las variables de la clase, Parte II

Sí, es una lectura larga, pero muy gratificante.

En resumen, yo he reutilizado el (obsoleto) VMT entrada llamada vmtAutoTable como una variable.Esta ranura en el VMT puede ser utilizado para almacenar cualquier valor de 4 bytes, pero si desea almacenar, siempre se puede asignar un registro con todos los campos que usted podría desear.

Las palabras clave que usted está buscando son de "clase var" - esto inicia un bloque de variables de clase en su declaración de la clase.Usted necesita para terminar el bloque con "var", si usted desea incluir otros campos después de ella (de lo contrario, el bloque puede ser terminado por un "privado", "público", "procedimiento", etc especificador).Por ejemplo

(Edición:Volver a leer la pregunta y se trasladó recuento de referencia en TMyClass - como usted no puede ser capaz de modificar el TMySharedObjectClass clase que desea compartir, si se trata de alguien de la biblioteca)

  TMyClass = class(TObject)
  strict private
    class var
      FMySharedObjectRefCount: integer;
      FMySharedObject: TMySharedObjectClass;
    var
    FOtherNonClassField1: integer;
    function GetMySharedObject: TMySharedObjectClass;
  public
    constructor Create;
    destructor Destroy; override;
    property MySharedObject: TMySharedObjectClass read GetMySharedObject;
  end;


{ TMyClass }
constructor TMyClass.Create;
begin
  if not Assigned(FMySharedObject) then
    FMySharedObject := TMySharedObjectClass.Create;
  Inc(FMySharedObjectRefCount);
end;

destructor TMyClass.Destroy;
begin
  Dec(FMySharedObjectRefCount);
  if (FMySharedObjectRefCount < 1) then
    FreeAndNil(FMySharedObject);

  inherited;
end;

function TMyClass.GetMySharedObject: TMySharedObjectClass;
begin
  Result := FMySharedObject;
end;

Por favor, tenga en cuenta lo anterior no es thread-safe, y puede haber mejores maneras de referencia de conteo (tales como el uso de Interfaces), pero esto es un simple ejemplo que debe empezar.Nota el TMySharedObjectClass puede ser sustituido por TLogLogger o lo que quieras.

Bueno, no es la belleza, pero funciona bien en Delphi 7:

TMyObject = class
pulic
    class function MySharedObject: TMySharedObject; // I'm lazy so it will be read only
end;

implementation

...

class function MySharedObject: TMySharedObject;
{$J+} const MySharedObjectInstance: TMySharedObject = nil; {$J-} // {$J+} Makes the consts writable
begin
    // any conditional initialization ...
   if (not Assigned(MySharedObjectInstance)) then
       MySharedObjectInstance = TMySharedOject.Create(...);
  Result := MySharedObjectInstance;
end;

Estoy actualmente usando para construir los únicos objetos.

Para lo que yo quiero hacer (una clase privada constante), el más bonito de la solución que se me ocurre (basado en las respuestas hasta ahora) es:

unit MyObject;

interface

type

TMyObject = class
private
  class var FLogger: TLogLogger;
end;

implementation

initialization
  TMyObject.FLogger:= TLogLogger.GetLogger(TMyObject);
finalization
  // You'd typically want to free the class objects in the finalization block, but
  // TLogLoggers are actually managed by Log4D.

end.

Tal vez un poco más orientado sería algo como:

unit MyObject;

interface

type

TMyObject = class
strict private
  class var FLogger: TLogLogger;
private
  class procedure InitClass;
  class procedure FreeClass;
end;

implementation

class procedure TMyObject.InitClass;
begin
  FLogger:= TLogLogger.GetLogger(TMyObject);
end;

class procedure TMyObject.FreeClass;
begin
  // Nothing to do here for a TLogLogger - it's freed by Log4D.
end;

initialization
  TMyObject.InitClass;
finalization
  TMyObject.FreeClass;

end.

Que podría tener más sentido si hay varias constantes de clase.

Dos preguntas que creo que deben ser respondidas antes de venir para arriba con un "perfecto" solución..

  • La primera, es si TLogLogger es thread-safe.Puede el mismo TLogLogger ser llamado desde varios subprocesos sin las llamadas a "sincronice"?Incluso si es así, el siguiente todavía se pueden aplicar
  • Son las variables de la clase thread-en-el alcance o verdaderamente global?
  • Si las variables de la clase son verdaderamente global, y TLogLogger no es seguro para subprocesos, puede ser mejor utilizar una unidad global threadvar para almacenar la TLogLogger (como mucho ya que no me gusta el uso de "global" vars en cualquier forma), por ejemplo

Código:

interface
type
  TMyObject = class(TObject)
  private
    FLogger: TLogLogger; //NB: pointer to shared threadvar
  public
    constructor Create;
  end;
implementation
threadvar threadGlobalLogger: TLogLogger = nil;
constructor TMyObject.Create;
begin
  if not Assigned(threadGlobalLogger) then
    threadGlobalLogger := TLogLogger.GetLogger(TMyObject); //NB: No need to reference count or explicitly free, as it's freed by Log4D
  FLogger := threadGlobalLogger;
end;

Editar:Parece que las variables de la clase a nivel mundial se almacena en vez de una instancia por cada subproceso.Ver esta pregunta para obtener más detalles.

En Delphi variables estáticas se implementan como tipos de variables constantes :)

Esto podría ser un poco engañoso.

procedure TForm1.Button1Click(Sender: TObject) ;
const
   clicks : Integer = 1; //not a true constant
begin
  Form1.Caption := IntToStr(clicks) ;
  clicks := clicks + 1;
end;

Y sí, otra posibilidad es el uso de una variable global en implementation parte de su módulo.

Esto sólo funciona si el modificador de compilador "Asignables Consts" está activada, a nivel mundial, o con {$J+} sintaxis (tnx Lars).

Antes de la versión 7, Delphi no tienen variables estáticas, usted tendría que usar una variable global.

Para hacerlo tan privada como sea posible, ponerlo en el implementation sección de la unidad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top