How one class can inherit from one interface and another class in Delphi

Asked

Viewed 2,201 times

0

I’m creating a small persistence class using RTTI. I created the attributes of fields separated by type Ex: Fieldstring[], Fieldinteger[] and etc.. At a certain point I need to go through the attributes to find a certain field, and here’s the question. Currently in the form that this, I would first have to check if the attribute is Fieldstring[], Fieldinteger[], and then compare with the Property 'Name' that each class has. So I thought about creating an Ifield-like interface with the Property 'Name' and then I would cast. The problem is that classes have to implement Tcustomattribute, Ex:

IField = interface
..
  property Name:string read GetName write SetName;
  property IsPk:string read GetIsPk write SetIsPk;
end;

TFieldString = class(TCustomAttribute, IField) 
...
  property Name:string read GetName write SetName;
  property IsPk:string read GetIsPk write SetIsPk;
end.

TFieldInteger = class(TCustomAttribute, IField) 
...
  property Name:string read GetName write SetName;
  property IsPk:string read GetIsPk write SetIsPk;
end.

and I don’t know how to do this.

  • I don’t understand what your doubt is, it seems that you are on the right track. How about asking the question the code you have done so far to better understand?

  • Yesterday after researching a lot, I found this link http://stackoverflow.com/questions/5353656/delphi-inherit-from-a-class-and-an-interface-adapter-pattern a very similar question, and the answer was that to be able to do this (inherit an interface and a class), the 3 Queryinterface, _Addref and _Release methods of Tinterfacedobject should be implemented in the class(s) daughter(s). But I don’t know if this is right.

  • Yes, that is correct. Every class that implements an interface needs to implement these three methods in order to be able to support the reference count memory management system and also so that the interface typing can be tested at certain times. In general, when implementing an Interface, the class is used as an ancestor TInterfacedObject, that already have these methods implemented, but you can also implement if necessary. Just be careful, TCustomAttribute serves as an ancestor for creating new annotation attributes, not for the use you want to give.

  • If the idea is to use the descending classes of TCustomAttribute to annotate the properties of your persistent class, so I recommend that you follow the compiler convention and call, for example, TFieldStringAttribute. Regarding your specific problem, it wasn’t clear to me because you need the interface. I could explain it better?

3 answers

0

I’m not sure if I understand your question (and I can’t comment on my reputation), but I will try to answer by my understanding of the initial question.

Delphi does not support multiple inheritance, but classes can implement multiple interfaces and you can delegate the interface execution so you can simulate multiple inheritance means.

Your interfaces need to be declared as Interface(IInterface) and its concrete classes as class(TInterfacedObject, IInterface). Following your example, it would look like this:

type

  IField = Interface(IInterface)
    // Declaração de métodos que deveram ser implementos pelas
    // classes que implementarem essa interface.
  end;

  TCustomAttribute = class(TInterfacedObject)
    // Declaração de métodos e atributos.
  end;

  TFieldString = class(TCustomAttribute, IField)
    // Declaração de métodos e atributos.
  end;

  TFieldInteger = class(TCustomAttribute, IField)
    // Declaração de métodos e atributos.
  end;

end.

Source: http://www.delphibasics.co.uk/Article.asp?Name=Interface

I hope I’ve helped!

0

I think it will now be clear. For example, in the mounting of the Insert

  for RttiProperty in RttiType.GetProperties do
    for RttiAttribute in RttiProperty.GetAttributes do
      if RttiAttribute is TTypeInteger then
      begin
        if not TTypeInteger(RttiAttribute).IsIdentity then
        begin
          strFields := strFields + TTypeInteger(RttiAttribute).Name  + ',';
          strValues := strValues + QuotedStr( IntToStr( RttiProperty.GetValue(TObject(Obj)).AsInteger ) ) + ',';
        end;
      end
      else if RttiAttribute is TTypeString then
      begin
        if Trim( RttiProperty.GetValue(TObject(Obj)).AsString ) <> '' then
        begin
          strFields := strFields + TTypeString(RttiAttribute).Name  + ',';
          strValues := strValues + QuotedStr(RttiProperty.GetValue(TObject(Obj)).AsString) + ',';
        end;
      end;
    end;

I go through all the attributes to get the name and value. Okay, but if I only wanted to get the names of the fields, I would still have to test the type of attribute, one by one. What I wanted was something like

if RttiAttribute is TField then
  strFields := strFields + TField (RttiAttribute).Name  + ',';

But to achieve this, the daughter class would have to implement Tfield and Tcustomattribute, but as it is not possible in DELPHI, I created the interface, but still could not compile, that’s when I sent the question.

0


Attributes in Delphi are only classes, when annotating a language construction with an attribute, what is done is to instantiate a class and link it to the construction for use by RTTI. Thus, you can avoid the interface (which will in fact be for you only headache) by means of an ancestor, as follows:

TFieldAttribute = class(TCustomAttribute)
private
  FName: string;
  FisPk: Boolean;
public
  constructor Create(const aName: string; aIsPk: Boolean=False);
  property Name:string read FName write FName;
  property IsPk: Boolean read FIsPk write FIsPk;
end;

TFieldStringAttribute = class(TFieldAttribute) 
end;

TFieldIntegerAttribute = class(TFieldAttribute) 
end;

And so for all attributes, however, I don’t understand why you need specific attributes by type, since Rtti already gives you the typing of each Property. If I were you I would have only the ancestral attribute (TFieldAttribute) to identify each of the mapped properties and use the type of that property itself to map to your persistence medium.

Now, if you have cases where there are different types between the Delphi property and the column in the database, then what you can do is create one more parameter in the attribute to say that. For example:

TFieldAttribute = class(TCustomAttribute)
private
  FName: string;
  FColumnType: string
  FIsPk: Boolean;
public
  constructor Create(const aName: string; aIsPk: Boolean=False); overload;
  constructor Create(const aName, aColumnType: string; aIsPk: Boolean=False); overload;
  property Name:string read FName write FName;
  property ColumnType: string read FColumnType write FColumnType;
  property IsPk: Boolean read FIsPk write FIsPk;
end;

In particular, I would have a proper attribute for PK. That would be:

TFieldAttribute = class(TCustomAttribute)
private
  FName: string;
  FColumnType: string
public
  constructor Create(const aName: string); overload;
  constructor Create(const aName, aColumnType: string); overload;
  property Name:string read FName write FName;
  property ColumnType: string read FColumnType write FColumnType;
end;

TPrimaryKeyAttribute = class(TFieldAttribute)
end;

There in your code what you need is to test the type of attribute using the operator is. In the case of a person-type class (typical example) would be:

TPessoa = class
private
  [PrimaryKey('Id')]
  FId: Long;
  [Field('Nome')]
  FNome: string;
  [Field('Dt_Nasc', 'VARCHAR')]
  FNascimento: TDateTime;
public
  property Id: Long read FId;
  property Nome: string read FNome write FNome;
  property Nascimento: TDateTime read FNascimento write FNascimento;
end;

I imagine this will suit you!

  • Actually I decided to separate exactly why, I’m trying to use SQL Server and SQLITE. But I will try to do what you suggested and create the Tfieldattribute leaving the interface aside.

  • In this way it worked very well, thank you!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.