I have this (defined in delphi source code):
TCanvasTextureMaterial = class(TCustomMaterial)
private
[Weak] FTexture: TTexture;
procedure SetTexture(const Value: TTexture);
protected
public
property Texture: TTexture read FTexture write SetTexture;
end;
how to override the setter SetTexture in descendant class?
In short, you can't.
The setter is declared as private and thus is not accessible to descendants. But even if it were accessible, it is not declared as virtual so it can't be override'n anyway.
The only thing a descendant could do in this case is (re)declare its own property with its own setter (it could even reuse the same Texture property name), eg:
type
TMyCanvasTextureMaterial = class(TCanvasTextureMaterial)
private
function GetMyTexture: TTexture;
procedure SetMyTexture(const Value: TTexture);
public
property Texture: TTexture read GetTexture write SetMyTexture;
end;
But, accessing the Texture property via a TCanvasTextureMaterial pointer will not call the descendant's setter, even if it does use the same property name:
var
TM: TCanvasTextureMaterial;
begin
TM := ...; // any TMyCanvasTextureMaterial object
// reading from TM.Texture returns TCanvasTextureMaterial.FTexture,
// it does not call TMyCanvasTextureMaterial.GetMyTexture()
// assigning to TM.Texture calls TCanvasTextureMaterial.SetTexture(),
// it does not call TMyCanvasTextureMaterial.SetMyTexture()
end;
Related
I am looking at the source code for CodeGear C++ Builder header files. In Source/vcl/forms.pas I find the following lines of code:
procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
var
Instance: TComponent;
begin
Instance := TComponent(InstanceClass.NewInstance); // 메타클래스 이용하여, 인스턴스화
TComponent(Reference) := Instance;
try
Instance.Create(Self); // 폼 생성
except
TComponent(Reference) := nil;
raise;
end;
if (FMainForm = nil) and (Instance is TForm) then
begin
TForm(Instance).HandleNeeded; // 윈도우핸들을 생성
FMainForm := TForm(Instance);
end;
end;
Contextually, what I think is happening is that this procedure creates an instance of type InstanceClass and returns that instance through Reference. In my call, InstanceClass is not TForm, so the second half doesn't matter.
I am confused by TComponent(Reference) := Instance;. Syntactically, what is happening here? Is this assignment by reference? Is this assigning a new TComponent with the instantiator argument Reference being assigned the value? Is TComponent() a type casting?
In the procedure’s signature the formal parameter reference does not indicate a data type, but is declared as a variable parameter.
Typeless parameters are not legal in Pascal, but permitted in dialects such as GNU Pascal and FreePascal.
There, such variable parameters accept actual parameters of any data type.
The compiler will not enforce/restrict permissible data types, but it must be addressable (i. e. literals are not permitted).
dataTypeName(expression) is indeed a typecast (also illegal in Pascal, but allowed by some dialects).
Specifically, dataTypeName(variableName) is a variable typecast.
It will be treated as if variableName was of the named data type.
This is necessary, because in this particular case reference has no associated data type.
No associated data type means, there is no agreed rule in how to access the variable in question (i. e. any read/write access is impossible).
The procedure probably creates an instance of TComponentClass, i. e. the data type of the parameter InstanceClass, but there you should really read the documentation of the NewInstance method.
I can only tell you it’s a descendant of TComponent otherwise it hardly makes sense to typecast to an unrelated class.
The Reference parameter is an untyped var parameter. In C++ terms, it is roughly equivalent to void*, ie the address of any variable can be bound to it. The code is type-casting the Reference to the equivalent of TComponent*& or TComponent** and then assigning the Instance variable (a TComponent* pointer) to the caller's passed variable.
The code is roughly equivalent to the following in C++ (except that metaclasses and virtual constructors don't exist in C++, so this code is actually not usable in C++):
void __fastcall TApplication::CreateForm(TComponentClass* InstanceClass, void* Reference)
{
// this just allocates a block of memory of the required size...
TComponent* Instance = static_cast<TComponent*>(InstanceClass->NewInstance());
// *static_cast<TComponent**>(Reference) = Instance;
reinterpret_cast<TComponent*&>(Reference) = Instance;
try
{
// This is calling the class' virtual constructor
// on the allocated memory. In C++, this would be
// similar to calling 'placement-new' if the class
// type was known statically here...
// new(Instance) InstanceClass->ClassType()(this);
Instance->Create(this);
}
catch (...)
{
// *static_cast<TComponent**>(Reference) = NULL;
reinterpret_cast<TComponent*&>(Reference) = NULL;
throw;
}
if ((!FMainForm) && (dynamic_cast<TForm*>(Instance) != NULL))
{
static_cast<TForm*>(Instance)->HandleNeeded();
FMainForm = static_cast<TForm*>(Instance);
}
}
// Application.CreateForm(TForm, Form1);
Application->CreateForm(__classid(TForm), reinterpret_cast<void*>(&Form1));
The following Delphi program calls method upon nil reference and runs fine.
program Project1;
{$APPTYPE CONSOLE}
type
TX = class
function Str: string;
end;
function TX.Str: string;
begin
if Self = nil then begin
Result := 'nil'
end else begin
Result := 'not nil'
end;
end;
begin
Writeln(TX(nil).Str);
Readln;
end.
However, in a structurally similar C# program, System.NullReferenceException will be raised, which seems to be the right thing to do.
namespace ConsoleApplication1
{
class TX
{
public string Str()
{
if (this == null) { return "null"; }
return "not null";
}
}
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine(((TX)null).Str());
System.Console.ReadLine();
}
}
}
Because TObject.Free uses such style, it seems to be "supported" to call method on nil reference in Delphi. Is this true ? (Let's suppose that in the if Self = nil branch, no instance field will be accessed.)
It is reasonable to call a method on a nil reference, subject to the following rules:
The method must not be virtual or dynamic. That is because virtual or dynamic methods are bound using the runtime type of the reference. And if the reference is nil then there is no runtime type. By way of contrast, non-virtual, non-dynamic methods are bound at compile time.
You are allowed to read the value of Self, for instance to compare it against nil.
In case Self is nil, then you must not refer to any instance variables.
To send data to multiple clients I create a TIdThreadSafeStringList in OnConnect and assign it to AContext->Data like so
AContext->Data = new TIdThreadSafeStringList
When the client disconnects, its stringlist is deleted in OnDisconnect like so
delete AContext->Data
However this results in an AV. If I zero the parameter, like so
delete AContext->Data
AContext->Data = NULL
the AV goes away. Is there some auto cleanup I'm not aware of?
Using C++ Builder 10.2.3.
Is there some auto cleanup I'm not aware of?
Yes. TIdContext derives from TIdTask, which owns the Data property. The TIdTask destructor is called after the OnDisconnect event and will free the Data object if it is not NULL.
Another (preferred) way to handle this situation is to instead derive a new class from TIdServerContext and add your TIdThreadSafeStringList to that class (and any other per-client custom functionality you want), eg:
class TMyContext : public TIdServerContext
{
public:
TIdThreadSafeStringList *MyList;
__fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList = NULL)
: TIdServerContext(AConnection, AYarn, AList)
{
MyList = new TIdThreadSafeStringList;
}
__fastcall ~TMyContext()
{
delete MyList;
}
//...
};
Then assign your class type to the server's ContextClass property at runtime before activating the server, eg:
__fastcall TMyForm::TMyForm(TComponent *Owner)
: TForm(Owner)
{
IdTCPServer1->ContextClass = __classid(TMyContext);
//...
}
Then, you can simply type-cast any TIdContext* pointer belonging to the server to your class type in order to access the TIdThreadSafeStringList (or other functionality):
static_cast<TMyContext*>(SomeIdContext)->MyList->...
This way, you can ignore the TIdContext::Data property altogether, or use it for other purposes, if desired.
well today i was re-writing some olds stuff here and got myself into a problem that i don't know the answer.
i created the following attribute:
Enumeration<T> = class(TCustomAttribute)
strict private
{ Private declarations }
FValues : TList<T>;
public
{ Public declarations }
constructor Create(const AValues : array of T);
destructor Destroy(); override;
public
{ Public declarations }
property Values : TList<T> read FValues;
end;
with that in mind, i can use this attribute just fine in the following class for example:
[Entity('tablename')]
TUser = class(TEntity)
strict private
[Column('idcolumnname')]
[PrimaryKey(True)]
Fid : TInteger;
[Column('typecolumnname')]
[Enumeration<string>(['A', 'B', 'C', 'D', '...'])]
Ftype: TEnumeration<string>;
end;
it is great that it worked but idk, it seems to me that this should'nt work, couse in my ignorance, delphi attributes expect only constant types and im not only using array as a paremeter but a generic one.
moving foward, i made this attribute:
Association = class(TCustomAttribute)
strict private
{ Private declarations }
FMasterKeys : TList<string>;
FDetailKeys : TList<string>;
public
{ Public declarations }
constructor Create(const AMasterKeys, ADetailKeys : array of string);
destructor Destroy(); override;
public
{ Public declarations }
property MasterKeys : TList<string> read FMasterKeys;
property DetailKeys : TList<string> read FDetailKeys;
end;
and tried to use on this class:
[Entity('tablename')]
TSuperUser = class(TEntity)
strict private
[Association(['masterkey'], ['detailkey'])]
Fuser : TAssociation<TUser>;
end;
i got a error [DCC Error] E2026 Constant expression expected.
ok, so in resume i just dont know whats happening why i can use a array of T as a attribute parameter and not a array of string for instance.
thx for any help in advance
Check your compiler warnings. It should say W1025 Unsupported language feature: 'custom attribute' for your "compiling code". So what you though compiles did in fact not. It just did not raise an error.
This is usually the case when an attribute class cannot be found because fact you cannot have generic attributes. And is still the case in XE7.
Bottom line: Even if it did compile your executable will not contain that attribute.
Ok, I have been using the excellent Delphi-Mocks Framework and I just encountered a problem. Let´s suppose I have the following interfaces:
IDepartment = Interface
['{F4915950-8F32-4944-A3B6-8E08F6C38ECC}']
function getID() : Integer;
function getName() : String;
property ID: Integer read getID;
property Name: String read getName;
end;
ISale = Interface
['{F4915950-8F32-4944-A3B6-8E08F6C38E77}']
function getAmmount() : Currency;
function getDepartment() : IDepartment;
property Ammount: Currency read getAmmount;
property Department : IDepartment getDepartment;
end;
Now, I am trying to test the Sale interface using DUnit and the Delphi-Mocks, and use it as follows:
procedure TMyTest.Test_Sale_HasDepartment;
var
mckDepartment : TMock<IDeparment>;
mckSale : TMock<ISale>;
begin
mckDepartment := TMock<IDepartment>.Create;
mckDepartment.Setup.WillReturn('My Department').When.Name;
mckDepartment.Setup.WillReturn(1).When.ID;
// Create a sale Mock
mckSale := TMock<ISale>.Create;
mckSale.Setup.WillReturn(100).When.Ammount;
//** Here´s there is where I don´t know how to add a "child mock"**
mckSale.Setup.WillReturn(TValue.From<IDepartment>(mckDepartment)).When.Department;
// What I am trying to get is the following:
WriteLn(mckSale.Instance.Department.Name); // Should return "My Department" but fails with an AV.
end;
So my question is: How can I add a child mock to an existing mocked Interface and call its methods and properties?
Thanks!
P.S. I am using Delphi XE2.
mckSale.Setup.WillReturnDefault('getDepartment', TValue.From(mckDepartment));