2013-11-15 16 views
6

Biz TStringList bazı ilişkili nesnelerle birlikte dizeleri ekleyebilirsiniz:TStringList'te İşaretçi ve TObject girişleri nasıl ayırt edilir?

list: TStringList; 
obj: MyObject; 

obj := MyObject.Create(); 
list.AddObject("real object", obj); 

Ayrıca basitçe yani Pointer, bir tamsayı değeri ile bir dize bağlamak için çok kullanışlı olabilir, örneğin:

list.AddObject("just an index", Pointer(7)); 
bir MyObject ya da sadece bir Pointer ise bu listedeki bir nesneye daha sonra erişim nasıl olduğunu öğrenmek isterseniz

? Böyle bir şey istiyorum:

for i := 0 to list.Count-1 do 
    if list.Objects[i] is MyObject then 
    begin 
    // ... 
    // Do something with list.Objects[i] 
    // ... 
    end; 

ama bu besbelli list.Objects [i] sadece Pointer ise ihlali erişmek yol açar. Şimdiden teşekkürler!

+7

iyi çözüm ikisini karıştırmak getirmemektir nesneleri saklamak istiyorsanız, nesneleri saklayın ve sonra 'nil' ile karşılaştırabilir veya 'Assigned' komutunu kullanabilirsiniz. Tamsayıları depolamak istiyorsanız, tamsayıları depolayın ('İşaretçi (7) 'ne yapar - bu bir * işaretçisini saklamıyor * derleyici şikayeti geçmesi için bir işaretçi olarak tamsayı typecast.) –

+1

Genel olarak, $ FFFF'den daha düşük adreslerin sahte nesneler olduğu varsayılabilir - nesneler değil, bu bellek aralığına okuma erişiminiz yok. Bunu yedeklemek için belgeleri bul –

+2

Bu soru ya merak ediyor (iyi) ya da pervasızlık (kötü). Gerçek kodda bu tür güvensiz veya bozuk fikir kullanmaktan kaçının, ya da kötü kötü şeyler olacak. Merak ettiysem, burada üstleneceğim gibi, o zaman bu konuyu düşünmek için kudos. Neden Tobjects'a yapışıp bir veri sahibi yapmıyorsunuz? Tmyintegerdata ... –

cevap

7

güvenle bir StringList içine hem tamsayılar ve nesneleri saklamak istiyorsanız, tamsayılar veya nesneleri tutmak için bir varyant konteyner sınıfını tanımlar. Aşağıda

kabaca bir test projesi dahil özetlenen böyle bir sınıftır. @DavidHeffernan doğru belirttiği gibi

unit VariantContainer; 

interface 

uses Variants,SysUtils; 

Type 
    TVariantContainer = class 
    private 
     FVariant : Variant; 
    public 
     constructor Create(aValue: Integer); overload; 
     constructor Create(aValue: TObject); overload; 
     function IsInteger: Boolean; 
     function IsObject: Boolean; 
     function AsObject: TObject; 
     function AsInteger: Integer; 
    end; 

implementation 

function TVariantContainer.AsInteger: Integer; 
begin 
    if not IsInteger then 
    raise Exception.Create('Variant is not Integer'); 
    Result := FVariant;  
end; 

function TVariantContainer.AsObject: TObject; 
begin 
    if not IsObject then 
    raise Exception.Create('Variant is not TObject'); 
    Result := TVarData(FVariant).VPointer; 
end; 

function TVariantContainer.IsInteger: Boolean; 
begin 
    Result := VarIsType(FVariant, varInteger); 
end; 

function TVariantContainer.IsObject: Boolean; 
begin 
    Result := VarIsType(FVariant, varByRef); 
end; 

constructor TVariantContainer.Create(aValue: Integer); 
begin 
    Inherited Create; 
    FVariant := aValue; 
end; 

constructor TVariantContainer.Create(aValue: TObject); 
begin 
    Inherited Create; 
    TVarData(FVariant).VType:= VarByRef; 
    TVarData(FVariant).VPointer:= aValue; 
end; 

end. 

program ProjectTestVariantContainer; 

{$APPTYPE CONSOLE} 
uses 
    Variants,SysUtils,Classes,VariantContainer; 

Type 
    TMyObj = class 
    s:String; 
    end; 

var 
    sList: TStringList; 
    o: TMyObj; 
    i: Integer; 
begin 
    o := TMyObj.Create; 
    o.s := 'Hello'; 
    sList := TStringList.Create; 
    sList.OwnsObjects := True; // List owns container objects 
    sList.AddObject('AnInteger',TVariantContainer.Create(3)); 
    sList.AddObject('AnObject',TVariantContainer.Create(o)); 
    for i := 0 to sList.Count-1 do 
    begin 
    if Assigned(sList.Objects[i]) then 
    begin 
     if TVariantContainer(sList.Objects[i]).IsInteger then 
     WriteLn(TVariantContainer(sList.Objects[i]).AsInteger) 
     else 
     if TVariantContainer(sList.Objects[i]).IsObject then 
     WriteLn(TMyObj(TVariantContainer(sList.Objects[i]).AsObject).s); 
    end; 
    end; 
    ReadLn; 
    o.Free; 
    sList.Free; 
end. 
+1

'TVariantContainer' nesnelerini serbest bırakmadan önce 'TStringList' öğesini serbest bırakmayı unutmayın (OwnsObjects' özelliğini true - XE2 +' ye ayarlamadığınız sürece) veya başka bir şey varsa bellek sızıntısı çekersiniz. –

+0

Evet, bu tür bir yeniden düzenleme işe yarayacaktı, ama bu tür değişikliklere ihtiyaç duymadan kontrol etmenin güzel ve kısa bir yolu olup olmadığını merak ediyordum. Önerin için teşekkürler. – grafd

+0

+1 ama aslında bir 'Varyant' bir 'Tamsayı' veya 'İşaretçi' yerine yedekleme alanı olarak kullanmak için bir neden var mı? –

5

Nesneye yalnızca noktasını belirten bir tamsayı eklemek kesinlikle mümkündür. Benzer şekilde, listenizdeki nesnenin zaten serbest bırakıldığı bir nesnesine bir işaretçinin olması mükemmel bir şekilde mümkündür.

Alt satırda, istediğin kadar, senin StringList tamsayı veya işaretçiler içeriyorsa bilmek hiçbir kurşun geçirmez yolu yoktur bellekte etrafa başlayabilir.

Farklı türleri karıştırmamanız gerektiğinden, bilmeniz gereken numaralı numaralı gereksinim de yoktur. Daha iyi bir yaklaşım, bir StringList içeren iki sınıf oluşturma ve çalışmak için dış sınıflar tipi güvenli hale olacaktır. Sorununuz o zaman sorun değil.

Örnekbu size böyle sınıfları uygulamak nasıl özünü vermek sadece olduğunu jeneriği

TStringIntegerMap = class 
    private FStringIntegerList: TStringList; 
    public 
    procedure Add(const Key: string; Value: Integer); 
    ... // Add the other required equivalent TStringlist methods 
    end; 

    TStringObjectMap = class 
    private FStringObjectList: TStringList; 
    public 
    procedure Add(const Key: string; Value: TObject); 
    ... // Add the other required equivalent TStringlist methods 
    end; 

Not desteklemiyorsa Delphi sürümü varsayarak.

+0

Eğer iki dizge listesini kolayca ikiye bölebilirsem, o zaman bu bir yol olurdu. Teşekkürler. – grafd

3

bir TObject aslında bir işaretçidir. Yani bir işaretçi ve bir TObject arasında ayrım yapmak için basit bir yol yoktur. Eğer bir nesne hakkında bir şeyler bilmek ve daha sonra bu bilgiyi almak gerekiyorsa

, bu bilgiyi uzağa atma. Daha sonra bir şey bilmeniz gerekiyorsa, onu hatırlayın.

0

, sınıf tipleri işaretçiler, bu nedenle anlama sahip ve depolanan bazı tip gösterge kalmadan bunları ayırt etmek yolu yoktur.

Ancak, sorusunu soracaksanız, "Nesne örneğine rastgele işaretçi noktaları verilip verilmediği nasıl belirlenir?" Bunun için bir çözüm yoktur: Ben bütün satır içi belgelerine gönderdiniz

/// <summary> 
/// Verifies that the argument points to valid object instance. 
/// </summary> 
/// <exception cref="EAccessViolation"> 
/// If segmentation fault occurs while reading VMT and/or its field from the 
/// specified memory address. 
/// </exception> 
/// <remarks> 
/// Delphi only, incompatible with FPC. 
/// </remarks> 
/// <example> 
/// <code> 
/// procedure TForm1.FormCreate(Sender: TObject); 
/// begin 
/// ShowMessage(BoolToStr(IsInstance(Self), True)); 
/// end; 
/// </code> 
/// </example> 
function IsInstance(Data: Pointer): Boolean; 
var 
    VMT: Pointer; 
begin 
    VMT := PPointer(Data)^; 
    Result := PPointer(PByte(VMT) + vmtSelfPtr)^ = VMT; 
end; 

, bu yüzden daha fazla yorum gereksiz olduğunu hissediyorum, ama olacak kesinlikle örnek Pointer(7) nedeni gibi neyin kasten geçersiz işaretçileri özetlemek istiyorum erişim ihlali hatası. pointer yüksek Word s (Windows.IS_INTRESOURCE makro olduğu gibi sadece aynı mantık sıfır Yani eğer bir ön kontrolü yapabilirsiniz.

function Is_IntResource(lpszType: PChar): BOOL; 
begin 
    Result := ULONG_PTR(lpszType) shr 16 = 0; 
end; 
İlgili konular