sharing the code on this question as reference: Delphi TPair Exception
How can I retrieve the key and value from a TObjectDictionary concrete entry without using TPair and without extracting/remove/delete the pair from the list ?
{$APPTYPE CONSOLE}
uses
SysUtils,
Generics.Defaults,
Generics.Collections;
type
TProduct = class
private
FName: string;
procedure SetName(const Value: string);
published
public
property Name: string read FName write SetName;
end;
type
TListOfProducts = TObjectDictionary<TProduct, Integer>;
{ TProduct }
procedure TProduct.SetName(const Value: string);
begin
FName := Value;
end;
var
MyDict: TListOfProducts;
MyProduct1: TProduct;
MyProduct2: TProduct;
MyProduct3: TProduct;
APair: TPair<TProduct, Integer>;
aKey: string;
begin
try
MyDict := TListOfProducts.Create([doOwnsKeys]);
MyProduct1 := TProduct.Create;
MyProduct1.Name := 'P1';
MyProduct2 := TProduct.Create;
MyProduct2.Name := 'P2';
MyProduct3 := TProduct.Create;
MyProduct3.Name := 'P3';
MyDict.Add(MyProduct1, 1);
MyDict.Add(MyProduct2, 2);
MyDict.Add(MyProduct3, 3);
//the code to look for a **concrete product** (ie: MyProduct1) goes here..
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Thanks.
=========================
= Code with the answer =
{$APPTYPE CONSOLE}
uses
SysUtils,
Generics.Defaults,
Generics.Collections;
type
TProduct = class
private
FName: string;
procedure SetName(const Value: string);
published
public
property Name: string read FName write SetName;
end;
type
TListOfProducts = TObjectDictionary<TProduct, Integer>;
{ TProduct }
procedure TProduct.SetName(const Value: string);
begin
FName := Value;
end;
var
MyDict: TListOfProducts;
MyProduct1: TProduct;
MyProduct2: TProduct;
MyProduct3: TProduct;
MySearchedProduct: TProduct; // From Answer.
APair: TPair<TProduct, Integer>;
aProductName: string;
begin
try
MyDict := TListOfProducts.Create([doOwnsKeys]);
MyProduct1 := TProduct.Create;
MyProduct1.Name := 'P1';
MyProduct2 := TProduct.Create;
MyProduct2.Name := 'P2';
MyProduct3 := TProduct.Create;
MyProduct3.Name := 'P3';
MyDict.Add(MyProduct1, 1);
MyDict.Add(MyProduct2, 2);
MyDict.Add(MyProduct3, 3);
Writeln('Enter the Product Name to search: ');
//the code to look for a **concrete product** goes here..
Readln(aProductName);
for MySearchedProduct in Mydict.Keys do
if (MySearchedProduct.Name = aProductName) then
break;
if MySearchedProduct.Name = aProductName then
WriteLn('I have found the product: ' + MySearchedProduct.Name)
else
WriteLn('I have not found a product with that name.');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
You can use The Keys and Values properties of MyDict.
In a loop like this:
var
MyProduct: TProduct;
Value: Integer;
begin
for Value in MyDict.Values do
writeln(Value);
for MyProduct in MyDict.Keys do
writeln(MyProduct.Name);
Or by index using ToArray:
writeln(MyDict.Keys.ToArray[1].Name);
writeln(MyDict.Values.ToArray[1]);
The Key and Value are saved in the dictionary as a TPair<TKey,TValue>. If you need to work with both key and value, the logical thing to do is use a TPair;
Looks like this:
for APair in MyDict do
begin
// Your stuff goes here.
end;
If for some reason you don't want to use TPair to extract the pairs you may use something like this, but this is absolutely not a good idea - you're doing lots of dictionary queries for no good reason:
for AKey in MyDict.Keys do
begin
AValue := MyDict[AKey];
// Do something with both AKey and AValue
end;
Looping through the keys could be extrimely slow if your dictionary contains lots of members. I suggest keeping the key in the Pair along with the real value. Considering the example provided it might look like this:
type
TListOfProducts = TObjectDictionary<TProduct, TPair<TProduct,Integer>>;
Related
We need to create a class using rtti with preset values. The values are taken from the attribute. All seems fine works exactly the time when you need to add value in the field. Find the right property and gets the value of the attribute is true. But the record is not operated. Tell me where wrong?
program DemoGenerator;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.Rtti;
Type
// My attribute
DemoDataAttribute = class(TCustomAttribute)
private
FGenerator: String;
public
constructor Create(Generator: String);
published
property Generator: string read FGenerator write FGenerator;
end;
//
TSomeType = Class
private
fPhone: string;
published
[DemoData('+1800764328')]
property Phone: string read fPhone write fPhone;
End;
//
TMegaSuperClass = Class
Function Go<T: Class, constructor>: T;
End;
Procedure Test;
var
LMsc: TMegaSuperClass;
New: TSomeType;
Begin
LMsc := TMegaSuperClass.Create;
try
New := LMsc.Go<TSomeType>;
Writeln('New.Phone: ' + New.Phone);
finally
LMsc.Free;
// New.Free;
end;
End;
{ DemoDataAttribute }
constructor DemoDataAttribute.Create(Generator: String);
begin
FGenerator := Generator;
end;
{ TMegaSuperClass }
function TMegaSuperClass.Go<T>: T;
var
LContext: TRttiContext;
LClass: TRttiInstanceType;
LProp: TRttiProperty;
LAttr: TCustomAttribute;
LField: TRttiField;
begin
// Init Rtti
LContext := TRttiContext.Create;
LClass := LContext.GetType(T) as TRttiInstanceType;
Writeln('LClass: ' + LClass.ToString);
// Result
Result := T.Create;
for LProp in LClass.GetProperties do
begin
Writeln('LProp: ' + LProp.ToString);
for LAttr in LProp.GetAttributes do
begin
Writeln('LAttr: ' + LAttr.ToString);
if LAttr is DemoDataAttribute then
Begin
Writeln('Attr value: ' + DemoDataAttribute(LAttr).Generator);
// How write value?
LProp.SetValue(#Result, DemoDataAttribute(LAttr).Generator);
End;
end;
end;
end;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
Test;
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Console output:
TSomeType
property Phone: string
DemoDataAttribute
value: +1800764328
Phone:
Like this:
LProp.SetValue(Pointer(Result), DemoDataAttribute(LAttr).Generator);
The first argument to SetValue is declared as Instance: Pointer. A class reference is simply the pointer to the instance, which is what you want.
I need to do a "smart" detection of a generic type and return as a string, but at the moment i don't understand why delphi put some strange identification on the PTypeInfo.Name property.
What i have so far is this:
program SO_29674887;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.TypInfo;
type
TypeResolver = class
strict private
class function ReCast<T>(const AValue) : T;
public
class function Format<T>(const AValue : T) : string;
end;
Compare = class
public
class function IsEqual<A;B>(const AVal : A; BVal : B) : string;
end;
{ TypeResolver }
class function TypeResolver.ReCast<T>(const AValue): T;
begin
Result := T(AValue);
end;
class function TypeResolver.Format<T>(const AValue: T): string;
var Info : PTypeInfo;
begin
Info := TypeInfo(T);
Result := 'undefined';
if(Info.Kind = tkInteger) then
begin
if(Info.Name = GetTypeName(TypeInfo(Byte))) then
Result := IntToStr(ReCast<Byte>(AValue))
else if(Info.Name = GetTypeName(TypeInfo(ShortInt))) then
Result := IntToStr(ReCast<ShortInt>(AValue))
else if(Info.Name = GetTypeName(TypeInfo(SmallInt))) then
Result := IntToStr(ReCast<SmallInt>(AValue))
else if(Info.Name = GetTypeName(TypeInfo(Integer))) then
Result := IntToStr(ReCast<Integer>(AValue));
end;
Result := Info.Name + ':' + Result;
end;
{ Compare }
class function Compare.IsEqual<A, B>(const AVal: A; BVal: B): string;
begin
Result := Format('%s = %s', [
TypeResolver.Format<A>(AVal),
TypeResolver.Format<B>(BVal)]);
end;
var PAUSE : string;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
WriteLn(Compare.IsEqual(0, 255));
WriteLn(Compare.IsEqual(-127, 127));
WriteLn(Compare.IsEqual(0, 65535));
WriteLn(Compare.IsEqual(-32768, 32767));
WriteLn(Compare.IsEqual(0, 4294967295));
WriteLn(Compare.IsEqual(-2147483648, 2147483647));
Readln(PAUSE);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
What happens here is that instead of give me the actual type name of the integer, delphi gives me strange identifier like :2, :4, :6, etc.
For instance:
Compare.IsEqual(0, 255); //Gives Info.Name = :2, Byte
Compare.IsEqual(-127, 127); //Gives Info.Name = ShortInt, :4
Compare.IsEqual(0, 65535); //Gives Info.Name = :6, Word
Compare.IsEqual(-32768, 32767); //Gives Info.Name = SmallInt, :8
Compare.IsEqual(0, 4294967295); //Gives Info.Name = :01, Cardinal
Compare.IsEqual(-2147483648, 2147483647); //Gives Info.Name = Integer, :21
So my question is how i can find the right type to cast if it seems to me that delphi doesn't provide any clue of the actual type when he deliver those identifiers and why exactly it gives those odd identifiers.
Here's my reproduction, somewhat shorter.
{$APPTYPE CONSOLE}
uses
System.TypInfo;
type
TypeResolver = class
public
class function Format<T>(const AValue : T) : string;
end;
class function TypeResolver.Format<T>(const AValue: T): string;
var
Info: PTypeInfo;
begin
Info := TypeInfo(T);
Result := Info.Name;
end;
begin
Writeln(TypeResolver.Format(0));
Writeln(TypeResolver.Format<Byte>(0));
Writeln(TypeResolver.Format(255));
Readln;
end.
In XE2 the output is:
:3
Byte
Byte
In XE6 and later the output is:
ShortInt
Byte
Byte
It looks as though the earlier versions of Delphi create a private type with an unspeakable name when inferring from a literal of 0. I cannot say why that should be so. Since the behaviour has changed, one can only assume that the Embarcadero engineers made the change to fix what they deemed to be a defect.
In other words, it would seem that the behaviour that you are observing is a bug.
My hypothesis that a private type is created is backed up by this program:
{$APPTYPE CONSOLE}
uses
System.SysUtils,
System.TypInfo;
type
TypeResolver = class
public
class function Format<T>(const AValue : T) : string;
end;
class function TypeResolver.Format<T>(const AValue: T): string;
var
Info: PTypeInfo;
TypeData: PTypeData;
begin
Info := TypeInfo(T);
Result := Info.Name;
if Info.Kind=tkInteger then begin
TypeData := GetTypeData(Info);
Result := Result + ', min = ' + IntToStr(TypeData.MinValue) +
', max = ' + IntToStr(TypeData.MaxValue);
end;
end;
begin
Writeln(TypeResolver.Format(0));
Readln;
end.
On XE2 the output is:
:3, min = 0, max = 127
On XE6 the output is:
ShortInt, min = -128, max = 127
So I think that this is an issue with generic type inference, that we can probably ascribe to a bug fixed in XE6.
I don't have any advice for how you should work around this because I don't know your actual problem. That said, comparing type names is generally something that is best avoided if possible.
It seems for me like you are trying to invent TValue which is located in RTTI.pas
I've rewritten your example using TValue.
Place a TMemo on a form and the following code:
uses
RTTI;
type
TypeResolver = class
public
class function ReCast<T>(const AValue): T;
class function Format<T>(const AValue: T): string;
end;
Compare = class
public
class function IsEqual<A; B>(const AValA: A; const AValB: B): string;
end;
class function Compare.IsEqual<A, B>(const AValA: A; const AValB: B): string;
begin
Result := Format('%s = %s', [TypeResolver.Format<A>(AValA), TypeResolver.Format<B>(AValB)]);
end;
{ TypeResolver }
class function TypeResolver.ReCast<T>(const AValue): T;
begin
Result := T(AValue);
end;
class function TypeResolver.Format<T>(const AValue: T): string;
begin
Result := TValue.From(AValue).TypeInfo.Name;
end;
procedure TForm13.FormCreate(Sender: TObject);
begin
Memo1.Lines.Add(Compare.IsEqual(0, 255));
Memo1.Lines.Add(Compare.IsEqual(-127, 127));
Memo1.Lines.Add(Compare.IsEqual(0, 65535));
Memo1.Lines.Add(Compare.IsEqual(-32768, 32767));
Memo1.Lines.Add(Compare.IsEqual(0, 4294967295));
Memo1.Lines.Add(Compare.IsEqual(-2147483648, 2147483647));
Memo1.Lines.Add(Compare.IsEqual(Form13, Memo1));
end;
I belive it does the thick for you?
Here is the output of above:
ShortInt = Byte
ShortInt = ShortInt
ShortInt = Word
SmallInt = SmallInt
ShortInt = Cardinal
Integer = Integer
TForm13 = TMemo
I'm making a Delphi VCL application. There is a class TStudent where I have two static functions: one which returns last name from an array of TStudent and another one which returns the first name of the student. Their code is something like:
class function TStudent.FirstNameOf(aLastName: string): string;
var i : integer;
begin
for i := 0 to Length(studentsArray) - 1 do begin
if studentsArray[i].LastName = aLastName then
begin
result := studentsArray[i].FirstName;
Exit;
end;
end;
result := 'no match was found';
end;
class function TStudent.LastNameOf(aFirstName: string): string;
var i : integer;
begin
for i := 0 to Length(studentsArray) - 1 do begin
if studentsArray[i].FirstName = aFirstName then
begin
result := studentsArray[i].LastName;
Exit;
end;
end;
result := 'no match was found';
end;
My question is how can I avoid writing almost same code twice. Is there any way to pass the property as parameter of the functions.
You can use an anonymous method with variable capture for this linear search. This approach gives you complete generality with your predicate. You can test for equality of any field, of any type. You can test for more complex predicates for instance an either or check.
The code might look like this:
class function TStudent.LinearSearch(const IsMatch: TPredicate<TStudent>;
out Index: Integer): Boolean;
var
i: Integer;
begin
for i := low(studentsArray) to high(studentsArray) do
begin
if IsMatch(studentsArray[i]) then
begin
Index := i;
Result := True;
exit;
end;
end;
Index := -1;
Result := False;
end;
Now all you need to do is provide a suitable predicate. The definition of TPredicate<T>, from the System.SysUtils unit, is:
type
TPredicate<T> = reference to function (Arg1: T): Boolean;
So you would code your method like this:
class function TStudent.GetFirstName(const LastName: string): string;
var
Index: Integer;
IsMatch: TPredicate<TStudent>;
begin
IsMatch :=
function(Student: TStudent): Boolean
begin
Result := Student.LastName=LastName;
end;
if not LinearSearch(IsMatch, Index) then
begin
raise ...
end;
Result := studentsArray[Index].FirstName;
end;
And likewise for GetLastName.
If your Delphi does not support anonymous methods then you won't be able to use variable capture and will have to find a more convoluted approach using of object method types. However, the basic idea will be much the same.
I haven't tested it, but I believe this could be one solution.
uses TypInfo;
class function TStudent.GetProperty( propertyName: string, searchValue : Variant ) : Variant ;
var i : integer;
begin
for i := 0 to Length(studentsArray) - 1 do begin
if GetPropValue( studentsArray[i], propertyName ) = searchValue
result := GetPropValue( studentsArray[i], propertyName );
end;
// your code in case of not finding anything
end;
If you are using Delphi 2010 or later, you could use Extended RTTI:
uses
..., Rtti;
type
TStudent = class
public
FirstName: String;
LastName: String;
class function GetNameOf(const aFieldToFind, aNameToFind, aFieldToReturn: string): string;
end;
class function TStudent.GetNameOf(const aFieldToFind, aNameToFind, aFieldToReturn: string): string;
var
i : integer;
ctx: TRttiContent;
StudentType: TRttiType;
Field: TRttiField;
Value: TValue;
begin
ctx := TRttiContext.Create;
StudentType := ctx.GetType(TStudent);
Field := StudentType.GetField(aFieldToFind);
for i := 0 to Length(studentsArray) - 1 do
begin
if Field.GetValue(#studentsArray[i]).AsString = aNameToFind then
begin
Result := StudentType.GetField(aFieldToReturn).GetValue(#studentsArray[i]).AsString;
Exit;
end;
end;
Result := 'no match was found';
end;
Then you can call it like this:
FirstName := TStudent.GetNameOf('LastName', 'Smoe', 'FirstName');
LastName := TStudent.GetNameOf('FirstName', 'Joe', 'LastName');
If you restructure the TStudent record a little, everything gets easier. Instead of having multiple string fields with different names, declare an array of strings with an enumeration range.
Give the enumeration meaningful names and add a search function where the search field and result field can be specified.
Type
TStudentField = (sfFirstName,sfLastName); // Helper enumeration type
TStudent = record
Field: array[TStudentField] of String;
class function SearchNameOf(searchField: TStudentField;
const aSearchName: string; resultField: TStudentField): string; static;
end;
Here is a test example:
program ProjectTest;
{$APPTYPE CONSOLE}
Type
TStudentField = (sfFirstName,sfLastName);
TStudent = record
Field: array[TStudentField] of String;
class function SearchNameOf(searchField: TStudentField; const aSearchName: string; resultField: TStudentField): string; static;
end;
var
studentsArray : array of TStudent;
class function TStudent.SearchNameOf(searchField: TStudentField; const aSearchName: string; resultField: TStudentField): string;
var
i : integer;
begin
for i := 0 to Length(studentsArray) - 1 do begin
if (studentsArray[i].Field[searchField] = aSearchName) then
begin
Result := studentsArray[i].Field[resultField];
Exit;
end;
end;
result := 'no match was found';
end;
begin
SetLength(studentsArray,2);
studentsArray[0].Field[sfFirstName] := 'Buzz';
studentsArray[0].Field[sfLastName] := 'Aldrin';
studentsArray[1].Field[sfFirstName] := 'Neil';
studentsArray[1].Field[sfLastName] := 'Armstrong';
WriteLn(TStudent.SearchNameOf(sfFirstName,'Neil',sfLastName));
ReadLn;
end.
You could use a several properties with index specifier backed by single getter function just as you do for regular array properties:
TDefault = class(TObject)
private
class function GetProp(const FindWhat: string; FindWhere: Integer): string;
static;
protected
/// <remarks>
/// You don't really need this one. I've added it for an illustration
/// purposes.
/// </remarks>
class property Prop[const FindWhat: string; FindWhere: Integer]: string read GetProp;
public
class property A[const FindWhat: string]: string index 0 read GetProp;
class property B[const FindWhat: string]: string index 1 read GetProp;
end;
{ ... }
class function TDefault.GetProp(const FindWhat: string; FindWhere: Integer): string;
begin
case FindWhere of
0: Result := 'Hallo!';
1: Result := 'Hello!';
end;
Result := Result + ' ' + Format('searching for "%s"', [FindWhat]);
end;
As you see, the class properties are just the same as instance properties.
And I must say its a pretty bad idea to perform a search in the property getter.
I have this spike to test TPair. You can copy+paste on a new Delphi XE Console-app. I have marked the line with the exception:
Project Project1.exe raised exception
class EAccessViolation with message
'Access violation at address 0045042D
in module 'Project1.exe'. Read of
address A9032D0C.
Any Idea ?
Thanks.
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
Generics.Defaults,
Generics.Collections;
type
TProduct = class
private
FName: string;
procedure SetName(const Value: string);
published
public
property Name: string read FName write SetName;
end;
type
TListOfProducts = TObjectDictionary<TProduct, Integer>;
{ TProduct }
procedure TProduct.SetName(const Value: string);
begin
FName := Value;
end;
var
MyDict: TListOfProducts;
MyProduct1: TProduct;
MyProduct2: TProduct;
MyProduct3: TProduct;
APair: TPair<TProduct, Integer>;
aKey: string;
begin
try
MyDict := TListOfProducts.Create([doOwnsKeys]);
MyProduct1 := TProduct.Create;
MyProduct1.Name := 'P1';
MyProduct2 := TProduct.Create;
MyProduct2.Name := 'P2';
MyProduct3 := TProduct.Create;
MyProduct3.Name := 'P3';
MyDict.Add(MyProduct1, 1);
MyDict.Add(MyProduct2, 2);
MyDict.Add(MyProduct3, 3);
APair := MyDict.ExtractPair(MyProduct1);
Writeln(APair.Key.Name); // <--- Error is Here.
Writeln(IntToStr(APair.Value));
Readln(aKey);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
This is a Delphi bug. TDictionary<TKey,TValue>.ExtractPair does not assign Result.
RRUZ located the bug in QC.
The code reads:
function TDictionary<TKey,TValue>.ExtractPair(const Key: TKey): TPair<TKey,TValue>;
var
hc, index: Integer;
begin
hc := Hash(Key);
index := GetBucketIndex(Key, hc);
if index < 0 then
Exit(TPair<TKey,TValue>.Create(Key, Default(TValue)));
DoRemove(Key, hc, cnExtracted);
end;
Result should be assigned when the call to DoRemove is made.
It's quite hard to work around this bug. ExtractPair is the only way to get an item out of the dictionary without destroying the key and so you have to call it. But since it won't return the extracted item, you need to first read the item, remember the value, and then call ExtractPair.
I'm currently creating a class to write and read arrays
Opening a file, closing a file all works well.
Also, I'm able to write an array towards a bin file.
But returning an array from the class is a bridge to far.
So far, ther're 2 issues where I'm not able to work around
1) in the public section
function ReadArrFromFile : array of single;
==> identifier expected but array found & incompatible types single and dynamic array
2) In the implementation with function Tbinfiles.ReadArrFromFile : array of single,
==> I always get E2029 Identifier expected but ARRAY found
For 1), if I define array of single in the main program it's not causing any problem
2) same for the ReadArrFromFile works fine on the main program
I'm working with codegear RAD delphi 2007 & windows vista.
unit UbinFiles;
interface
type
TBinFiles = Class
private
pFileName : String; // File name (FILENAME.bin)
pFileType : string; // File type (of .. )
pFileLoc : string; // FileLocation path
pMyarr : array of single; // array to receive / provide results
pArrLen : integer; // To define arraylength
pFKA : file; // File Known As or the internal name
pRecsWritten : integer; // # of blocks written towards file
pRecsRead : integer; // # of blocks read from file
public
procedure SetFname(const Value: String);
procedure SetFtype(const Value: String);
procedure SetFLoc(const Value: String);
procedure SetArrLen(const Value: integer);
constructor Create; overload;
constructor Create(Fname : String); overload;
constructor Create(Fname : String ; Ftype : string); overload;
constructor Create(Fname : String ; Ftype : string ; FLoc : String); overload ;
procedure OpenMyFile;
procedure CloseMyFile;
procedure Write2MyFile(Myarr : array of single );
procedure ReadFromMyFile;
function CheckBackSpace(MyPath : string) : string ;
procedure TSTreadAnArray(Myarr : array of single);
//---first problem
function ReadArrFromFile : array of single;
published
property Fname : String read pFileName write SetFname;
property Ftype : String read pFileType write SetFtype;
property FLoc : String read pFileLoc write SetFLoc;
property ArrLen : integer read pArrLen write SetArrLen;
end;
implementation
uses
Dialogs, SysUtils, StrUtils; // controls required for this class
//
//---Constructors-----------------------------
//
constructor TBinFiles.Create; // void constructor
begin
inherited;
self.pFileName := 'MyBinary';
self.pFileType := '';
self.pFileLoc := 'C:\Users\';
self.pRecsWritten := 0;
self.pRecsRead := 0;
end;
constructor TBinFiles.Create(Fname: String); // contructor + Fname
begin
self.pFileName := Fname;
self.pFileType := '';
self.pFileLoc := 'C:\Users\';
self.pRecsWritten := 0;
self.pRecsRead := 0;
end;
constructor TBinFiles.Create(Fname: String ; Ftype : string); // constructor etc..
begin
self.pFileName := Fname;
self.pFileType := Ftype;
self.pFileLoc := 'C:\Users\';
self.pRecsWritten := 0;
self.pRecsRead := 0;
end;
constructor TBinFiles.Create(Fname: String ; Ftype : string ; FLoc : string);
begin
self.pFileName := Fname;
self.pFileType := Ftype;
self.pFileLoc := CheckBackSpace(FLoc);
self.pRecsWritten := 0;
self.pRecsRead := 0;
end;
//
//----setters---------------------------------------
//
procedure TBinFiles.SetFname(const Value: String); // pFileName
begin
pFileName := Value;
end;
procedure TBinFiles.SetFtype(const Value: String); // pFileType
begin
pFileType := Value;
end;
procedure TBinFiles.SetFLoc(const Value: String); // pFileLoc
begin
pFileLoc := Value;
end;
procedure TBinFiles.SetArrLen(const Value: integer);
begin
pArrLen := Value;
end;
//
//---general functions / procs----
//
procedure Tbinfiles.OpenMyFile;
begin
try
AssignFile(self.pFKA, self.pFileLoc + self.pFileName +'.bin');
ReWrite(self.pFKA);
except
on E : Exception do
begin
ShowMessage(E.ClassName+' error raised, with message : '+E.Message);
end;
End;
end;
procedure Tbinfiles.CloseMyFile;
begin
CloseFile(self.pFKA);
End;
procedure Tbinfiles.Write2MyFile(Myarr : array of single );
begin
BlockWrite(self.pFKA, Myarr, 1,self.pRecsWritten);
End;
procedure Tbinfiles.ReadFromMyFile;
begin
BlockRead(self.pFKA, self.pMyarr, 1,self.pRecsread);
End;
//------second problem----------------------------------------------<<<<<< doesn't work
function Tbinfiles.ReadArrFromFile : array of single ;
begin
BlockRead(self.pFKA, self.pMyarr, 1,self.pRecsread);
End;
function Tbinfiles.CheckBackSpace(MyPath : string) : string ;
begin
if AnsiRightStr(MyPath, 1) = '\'
then Result := MyPath
else Result := MyPath + '\'
;
end;
procedure Tbinfiles.TSTreadAnArray(Myarr : array of single );
var i:integer;
begin
for i := 0 to high(Myarr) do
begin
showmessage('Element ' + intToStr(i)+ floatToStr(MyArr[i]) );
end;
end;
end.
You can't have an array as a property, but you can have array properties:
TMyObject = class
private
function GetSingleArray(aIndex: Integer): Single;
procedure SetSingleArray(aIndex: Integer; const Value: Single);
function GetSingleArrayCount: Integer;
procedure SetSingleArrayCount(const Value: Integer);
public
property SingleArray[aIndex: Integer]: Single read GetSingleArray write SetSingleArray;
//returns or sets the length of the single array
property SingleArrayCount: Integer read GetSingleArrayCount write SetSingleArrayCount;
end;
You can use a named type - try TSingleDynArray from unit Types.
However using array properties (see The_Fox's answer) might be more appropriate.
1)At first declare array type..
type
TpMyarr = array of single;
...and than yo can do:
function ReadArrFromFile : TpMyarr;
2)Before writing in dinamic array call SetLength first.
3)There is no need to use 'self.' in your program!
4)Instead BlockRead/BlockWrite use TFileStream delphi class.