Attribute constructor with array parameter - delphi

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.

Related

Does Delphi officially support calling an object method from a nil object? [duplicate]

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.

how to override a private property setter?

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;

Identifier not found on compiler (Free Pascal)

I'm trying to change the color of a panel on Lazarus on mouse hover.
I try run this code on Lazarus:
unit test;
{$mode objfpc}{$H+}
interface
uses
[...]
type
{ Tvendas_menu }
Tvendas_menu = class(TForm)
[...]
procedure StartMouseEnter(Sender: TObject);
[...]
private
{ private declarations }
public
{ public declarations }
end;
var
[...]
implementation
[...]
procedure Tvendas_menu.StartMouseEnter(Sender: TObject);
begin
Start.Color := $00E7E7E7;
end;
[...]
But when compiling the program show the following error code:
Error: Identifier not found "Start"
I'm sure that "Start" is the name of the panel on Object Inspector and .lfm file.
I try to change "Start" to another name but the error still occurs.
Thanks!
When you add controls to the form they automagically get added under the form class, in your case they would appear under Tvendas_menu = class(TForm).
One possibility of the error could be because the line Start is missing, you should have something like:
type
Tvendas_menu = class(TForm)
Start: TPanel;
private
{ Private declarations }
public
{ Public declarations }
end;
To resolve this, try adding the line Start: TPanel; like above if it is not present.
One other option you have is to view the form in text view (.lfm for Lazarus and .dfm for Delphi) and find the reference block for Start, it may look something like:
object Start: TPanel
Left = 152
Top = 248
Width = 185
Height = 41
Caption = 'Start'
TabOrder = 1
end
Delete that and then return back to Form view.
Then you can try adding a new panel to the form and naming it Start, then you just need to link your event handlers back to the new control.
As a side tip, naming a control Start is not really very useful, maybe think of a better named identifier such as panStart.

How to use mocks in child classes in Delphi using Delphi-Mocks framework

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));

Returning complex types (Classes, Arrays of Classes) using JCL and CLR

I first referenced this question to get started, but reached a roadblock when trying to return a Class or Array of a Class using a .NET Assembly in Delphi XE.
Consider the following:
//C#
[ComVisible(true)]
public class Person {
public int Id;
public string Name;
}
public class SomeClass
{
public SomeClass() {}
public Person[] GetPersons()
{
//some code
}
}
//Delphi
type TPerson = class
Id : Integer;
Name : string;
end;
How do I make sense of the data that is returned from GetPersons() which I can assign to an array of TPerson in Delphi?
You can't, at least not that way. Delphi and .NET have different object models, and different string types, which aren't compatible with each other. If you want to pass objects between .NET and Delphi modules, the best way is probably to use COM.

Resources