问题
I would like to clean concrete classes from interface section, but the compiler doesn't allow to make it. Whether there is an opportunity to make so that the external code couldn't address to concrete classes?
unit Unit2;
interface
uses
System.SysUtils
, System.Generics.Collections
, System.Win.Registry
;
type
IMyRegistry<T> = interface
procedure WriteValue(const Key, Ident: string; const AValue: T);
function ReadValue(const Key, Ident: string; const ADefVal: T): T;
end;
TMyRegistry<T> = class abstract(TInterfacedObject, IMyRegistry<T>)
strict private
class var FDefaultReg: IMyRegistry<T>;
class var FRegistry: TRegistry;
class var FDefKey: string;
private
class function GetDefKey: string;
class function GetInstance: IMyRegistry<T>; static;
protected
procedure WriteValue(const Key, Ident: string; const AValue: T); virtual; abstract;
function ReadValue(const Key, Ident: string; const ADefVal: T): T; virtual; abstract;
function GoToKey(const AKeyName: string; const ACreateKey: Boolean = False): Boolean;
class property InnerRegistry: TRegistry read FRegistry;
public
class constructor Create;
class destructor Destroy;
class property Default: IMyRegistry<T> read GetInstance;
end;
TDefaultMyRegistry<T> = class(TMyRegistry<T>)
protected
procedure WriteValue(const Key, Ident: string; const AValue: T); override;
function ReadValue(const Key, Ident: string; const ADefVal: T): T; override;
end;
TMyRegistryFactory = class
public
class function GetMyRegistryConcrete<T>: IMyRegistry<T>;
end;
//TMyRegInteger = class(TMyRegistry<Integer>)
//protected
//procedure WriteValue(const Key, Ident: string; const AValue: Integer); override;
//function ReadValue(const Key, Ident: string; const ADefVal: Integer): Integer; override;
//end;
EMyRegGenericException = class(Exception);
EMyRegTypeNotSupported = class(EMyRegGenericException);
const
cCompanyName = 'MyCompany';
cProgramName = 'MyProgram';
resourcestring
SRegTypeNotSupported = 'Operations are not supported for values of type "%s"';
implementation
uses
Winapi.Windows
, System.TypInfo;
type
TMyRegInteger = class(TMyRegistry<Integer>)
protected
procedure WriteValue(const Key, Ident: string; const AValue: Integer); override;
function ReadValue(const Key, Ident: string; const ADefVal: Integer): Integer; override;
end;
{ TMyRegistry<T> }
class constructor TMyRegistry<T>.Create;
begin
inherited;
FDefaultReg := TDefaultMyRegistry<T>.Create;
FRegistry := TRegistry.Create;
FRegistry.RootKey := HKEY_CURRENT_USER;
FDefKey := GetDefKey;
end;
class destructor TMyRegistry<T>.Destroy;
begin
FreeAndNil(FRegistry);
inherited;
end;
class function TMyRegistry<T>.GetDefKey: string;
const
cDefKey = '\Software\%s\%s\';
begin
Result := Format(cDefKey, [cCompanyName, cProgramName]);
end;
class function TMyRegistry<T>.GetInstance: IMyRegistry<T>;
begin
Result := FDefaultReg;
end;
function TMyRegistry<T>.GoToKey(const AKeyName: string; const ACreateKey: Boolean): Boolean;
var
sDestKeyName: string;
begin
sDestKeyName := FDefKey + AKeyName;
Result := FRegistry.OpenKey(sDestKeyName, False);
if not Result and ACreateKey then
Result := FRegistry.OpenKey(sDestKeyName, ACreateKey);
end;
{ TDefaultMyRegistry<T> }
function TDefaultMyRegistry<T>.ReadValue(const Key, Ident: string; const ADefVal: T): T;
var
tmpReg: IMyRegistry<T>;
begin
Assert(TypeInfo(T) <> nil, 'Тип не определен');
Result := ADefVal;
if GoToKey(Key) then
if InnerRegistry.ValueExists(Ident) then
begin
tmpReg := TMyRegistryFactory.GetMyRegistryConcrete<T>;
if Assigned(tmpReg) then
Result := tmpReg.ReadValue(Key, Ident, ADefVal);
end;
end;
procedure TDefaultMyRegistry<T>.WriteValue(const Key, Ident: string; const AValue: T);
var
tmpReg: IMyRegistry<T>;
begin
inherited;
if GoToKey(Key, True) then
begin
tmpReg := TMyRegistryFactory.GetMyRegistryConcrete<T>;
if Assigned(tmpReg) then
tmpReg.WriteValue(Key, Ident, AValue);
end;
end;
{ TMyRegistryFactory }
class function TMyRegistryFactory.GetMyRegistryConcrete<T>: IMyRegistry<T>;
begin
Result := nil;
if TypeInfo(T) = TypeInfo(Integer) then
Result := IMyRegistry<T>(IMyRegistry<Integer>(TMyRegInteger.Create)) //<<< [DCC Error] Unit2.pas(155): E2506 Method ...
else
raise EMyRegTypeNotSupported.CreateFmt(
SRegTypeNotSupported, [PTypeInfo(TypeInfo(T))^.Name]);
end;
{ TMyRegInteger }
function TMyRegInteger.ReadValue(const Key, Ident: string;
const ADefVal: Integer): Integer;
begin
Result := InnerRegistry.ReadInteger(Ident);
end;
procedure TMyRegInteger.WriteValue(const Key, Ident: string; const AValue: Integer);
begin
inherited;
InnerRegistry.WriteInteger(Ident, AValue);
end;
end.
If to move a class declaration 'TMyRegInteger' to implementation section, then the compiler reports about an error: "[DCC Error] Unit2.pas(155): E2506 Method of parameterized type declared in interface section must not use local symbol '.TMyRegInteger'"
in
class function TMyRegistryFactory.GetMyRegistryConcrete<T>: IMyRegistry<T>;
begin
Result := nil;
if TypeInfo(T) = TypeInfo(Integer) then
Result := IMyRegistry<T>(IMyRegistry<Integer>(TMyRegInteger.Create)) //<<<<<
来源:https://stackoverflow.com/questions/32988147/is-it-possible-to-move-the-descendant-of-generic-class-to-implementation-section