unit MyClass;
interface
uses Dialogs,
SysUtils;
type
TMyClass = class
private
FieldA: Integer;
FieldB: string;
public
constructor Create();
procedure Test;
end;
implementation
{ TMyClass }
constructor TMyClass.Create();
begin
inherited;
FieldA := 123;
FieldB := 'TEST';
end;
procedure TMyClass.Test;
begin
ShowMessage(IntToStr(FieldA));
ShowMessage(FieldB);
end;
end.
Yukarıdaki Uniti projeye ekledikten sonra, TMyClass sınıfından bir nesne oluşturup,
private bölümde bulunan
FieldA ve
FieldB alanlarına erişerek değerlerinin değiştirilmesi istenmektedir.
Kısıtlamalar:
- MyClass unit'inde ve TMyClass sınıfında herhangi bir kod değişikliği yapmak yasak.
- TMyClass sınıfından bir alt sınıf (sub class) oluşturmak ve kullanmak yasak.
- Class helper vs kullanmak yasak.
Test methodunu çağırdığımızda 123 ve TEST değerleri yerine başka değerler görmemiz gerekiyor.
var
MyObject: TMyClass;
begin
MyObject := TMyClass.Create;
{
sihirli kodlar buraya yazılacak
}
MyObject.Test;
MyObject.Free;
end;
Kolay gelsin.
var
MyObject: TMyClass;
begin
MyObject := TMyClass.Create; //MyObject nesnesi sınıf elemenlarının adreslerinin tutulduğu adresi tutuyor.
integer(pointer(nativeint((@MyObject)^)+4)^):=65;
//önce MyObject 'in adresine (@),ardından elemanların adreslerinin tutulduğu adrese(^),sonra 1.elemanın adresine(+4) ve nihayet elemanın kendisinine(^) ulaşıyoruz.
string(pointer(nativeint((@MyObject)^)+8)^):='Özel Alan Değişti.';
//önce MyObject 'in adresine (@),ardından elemanların adreslerinin tutulduğu adrese(^),sonra 2.elemanın adresine(+8) ve nihayet elemanın kendisinine(^) ulaşıyoruz.
//Not: Burada elemanın boyutunun bir önemi yok,adresler tutulduğu için 4'er bayt ilerlemek eleman adreslerini dolaşmak anlamına geliyor;çünkü adresler 4 byte değerindedir.
MyObject.Test;
MyObject.Free;
end;
type
TMyClassHook = class(TMYClass);
.
.
.
procedure TForm1.Button1Click(Sender: TObject);
var
a : TMyClass;
begin
a := TMyClass.Create();
TMyClassHook(a).FieldA := 1234;
TMyClassHook(a).FieldB := 'asd';
Memo1.Lines.Add(a.FieldB);
Memo1.Lines.Add(IntToStr(a.FieldA));
end;
var
MyObject : TMyClass;
AValue : TValue;
begin
MyObject := TMyClass.Create;
AValue := TValue.FromVariant(666);
TRttiContext.Create.GetType(TMyClass).GetField('FieldA').SetValue(MyObject, AValue);
AValue := TValue.FromVariant('DelphiCan');
TRttiContext.Create.GetType(TMyClass).GetField('FieldB').SetValue(MyObject, AValue);
MyObject.Test;
MyObject.Free;
Hakan Bey Yazdığım 2. madde bulunan sub class kullanım kısıtlamasını gözardı etmişsiniz.
RTTI kullanımını kısıtlamak aklıma gelmediği için Ali Bey'in cevabı da kabul edilebilir.
Savaş hocam'ın açıklamaları muazzam. Ben de açık halini paylaşayım.
var
MyObject: TMyClass;
P: PByte;
begin
MyObject := TMyClass.Create;
P := PByte(MyObject);
Inc(P, SizeOf(Pointer));
PINT(P)^ := 321;
Inc(P, SizeOf(Pointer));
PString(P)^ := 'XXXX';
MyObject.Test;
MyObject.Free;
end;
Zaman ayırıp cevap yazan arkadaşlara teşekkür ederim.
Cevap veren tüm arkadaşların ellerine sağlık. Aynı zamanda bu tarz konuları açan ve destekleyen ismail kardeşime de teşekkür ediyorum.
Internette dolaşırken tesadüfen gördüğüm
bir kod içerisinde tam da bu soruya uygun bir cevap buldum
Yeni bir sınıf tanımlıyoruz ve bu sefer ulaşmak istediğimiz değerleri
public tanımlıyoruz:
TMyClassAccessor = class
public
FieldA: integer;
FieldB: string;
end;
Kullanımı:
var
MyObject: TMyClass;
begin
MyObject := TMyClass.Create;
TMyClassAccessor(MyObject).FieldA := 321;
TMyClassAccessor(MyObject).FieldB := 'TSET';
MyObject.Test;
MyObject.Free;
end;