microtronx 38 Posted November 11, 2020 Hi, which is the simpliest way to add new properties to a existing component in runtime? Lets say we have a tDataset and I want to att a fList:tList to it in runtime, if it does not exists. Is this possible? 1 Share this post Link to post
Arnaud Bouchez 407 Posted November 11, 2020 (edited) Not possible, as François wrote, and also not useful. What would be the purpose of adding new properties at runtime? How to you access them? If what you expect is to make public something private, then you can trick the class by inheriting it locally, publishing the properties, then hard-casting to your type. If what you expect is to add some fields to an existing class, it is not possible because the class instance size if hardcoded during compilation. If what you expect is to have some additional information linked to an existing class, maintain a dictionary/map with the instances, and create a co-object associated with the main object lifetime, in which you put the needed data. If what you expect is to have some additional information linked to an existing class (as a variation to the previous item), inherit from this class or even better nest this class into a main owner class/TComponenet, which will have your expected behavior. Edited November 11, 2020 by Arnaud Bouchez Share this post Link to post
Remy Lebeau 1394 Posted November 11, 2020 7 hours ago, microtronx said: Hi, which is the simpliest way to add new properties to a existing component in runtime? That is not possible. Delphi is a compiled language, you can't alter object layouts at runtime. The best you could do is store your data in another class/record object that you create at runtime, and then use the component's Tag property to point at that object. Share this post Link to post
A.M. Hoornweg 144 Posted November 11, 2020 1 hour ago, Remy Lebeau said: That is not possible. Delphi is a compiled language, you can't alter object layouts at runtime. The best you could do is store your data in another class/record object that you create at runtime, and then use the component's Tag property to point at that object. Please don't misuse "tag" for pointers. I'd rather use a tDictionary<tcomponent, tSomethingelse> to store associations between components and objects. It's much more universal and transparent. 2 1 Share this post Link to post
microtronx 38 Posted November 12, 2020 10 hours ago, A.M. Hoornweg said: Please don't misuse "tag" for pointers. I'd rather use a tDictionary<tcomponent, tSomethingelse> to store associations between components and objects. It's much more universal and transparent. I have created a global "tDictionary<tComponent, tmxEvents>" and a small helper function to return an entry or add an entry if it does not exists ... working perfect. Thanks for your tips guys. Share this post Link to post
Remy Lebeau 1394 Posted November 12, 2020 (edited) 23 hours ago, A.M. Hoornweg said: Please don't misuse "tag" for pointers. There is nothing wrong with using the Tag property for pointers. It is specifically intended to hold user-defined data, and intentionally pointer-sized because storing pointers in it is very common practice. It is even documented that way Quote Tag has no predefined meaning. The Tag property can store any additional integer value for the convenience of developers. Often, Tag stores a pointer. A Tag value can be typecast to the appropriate pointer type. Notice that on 64-bit platforms, all pointer types are 8 bytes in size, while on 32-bit platforms, pointer types are 4 bytes. These pointer sizes correspond to sizes of NativeInt integral values on 64-bit and 32-bit platforms. So, there is no misuse here. 23 hours ago, A.M. Hoornweg said: I'd rather use a tDictionary<tcomponent, tSomethingelse> to store associations between components and objects. That is your choice, and it will certainly work. I prefer to use the Tag instead, it offers a 1:1 relationship without any overhead of a lookup table. Edited November 12, 2020 by Remy Lebeau Share this post Link to post
A.M. Hoornweg 144 Posted November 12, 2020 "tag" can fit a simple pointer-sized reference, sure, but you still need to handle ownership of the object. Also, "tag" has no way of knowing if the object it points to is still valid, you may need to clear the tag if the object is freed. That means writing boilerplate code. TDictionary and tObjectdictionary are "better" because they can handle object ownership. TDictionary<T> can contain managed objects such as strings, interfaces and (I suspect) even anonymous methods. If tDictionary manages the lifetime of the objects, there can't possibly be an invalid association and it reduces boilerplate code. Share this post Link to post
Remy Lebeau 1394 Posted November 13, 2020 6 hours ago, A.M. Hoornweg said: "tag" can fit a simple pointer-sized reference, sure, but you still need to handle ownership of the object. Also, "tag" has no way of knowing if the object it points to is still valid, you may need to clear the tag if the object is freed. That means writing boilerplate code. All of that applies to T(Object)Dictionary, as well. If the TComponent or associated object is freed, you have to manually remove them from the dictionary to avoid dangling pointers. 6 hours ago, A.M. Hoornweg said: TDictionary and tObjectdictionary are "better" because they can handle object ownership. TDictionary doesn't support object management at all. TObjectDictionary, on the other hand, simply lets you specify whether the dictionary owns the objects stored as Keys/Values, in that if an entry is removed/altered in the dictionary, the associate Key/Value objects are freed automatically as needed. But, if those same objects are freed external to the dictionary, the dictionary entry that refers to them is not removed automatically. You still have to handle that manually. 6 hours ago, A.M. Hoornweg said: If tDictionary manages the lifetime of the objects, there can't possibly be an invalid association The likelihood is reduced, but not eliminated. It is still possible to have invalid references, if you are not careful. 6 hours ago, A.M. Hoornweg said: and it reduces boilerplate code. In your code, perhaps, but not the overhead needed to manage the dictionary elements, hash tables, etc. You are actually adding runtime overhead to the program just to gain writing simpler code. But whatever. It is up to each user to decide whatever they are comfortable using. Share this post Link to post
A.M. Hoornweg 144 Posted November 13, 2020 tDictionary<T> does manage the lifetime of refcounted objects (interfaces, strings, anonymous methods, ...). If it's in the dictionary, then that's proof that the object is still alive. Try achieving that with "tcomponent.tag!"... tObjectlist<T> can manage the lifetime of plain vanilla tObject and the user can specify whether he his wishes this behavior or not in the constructor. If yes, no dangling pointers, because the user is not supposed to free manually. Of course the user is not protected from doing silly stuff like still freeing the objects manually. That's life. Yes dictionaries add a few dozen KB to the executable. But hey, RTTI adds a megabyte or so of metadata and for many/most of us it's only dead weight. If there's one single place in the compiler chain where we should be given more control, it's there. Anyway, I myself have stopped using Tag for pointers to objects because dictionaries made my life much easier. I find myself often using strings as a key because I totally like it when the code tells what it's doing. Tag will only store a fixed pointer, but a dictionary will let me query if an object exists and where it is stored, without any ugly typecasting, in a compact and legible way. 🙂 procedure tform1.Button1click(sender:tobject); var callback:tReportShowEvent; begin if ReportDict.TryGet('CurrentReport',callback) then Callback(); end; Share this post Link to post
Dave Novo 51 Posted November 21, 2020 The other issue I have with .Tag is that once things get complex, it is hard to know who is (ab)using the Tag property for what. You have a brilliant idea to use the .Tag of some object to associate it with another object only to find out 3 weeks and 20 hard to reproduce crashes later that someone else is also using the .Tag under certain rare circumstances for other associations. Having explicit lists to hold different types of associations eliminates those problems. If you get in the habit of using .Tag all the time, you cannot even do a search for .Tag to see who is using the .Tag of your object because the search hits so many times. We used to use .Tag quite a bit but moved away from it primarily for that reason. If its a class that we control, we will make a property just for the association, that is appropriately named. If it is a class from a 3rd party library we make a list like @A.M. Hoornweg suggested. Of course, that is just our preference based on our personal experience. Your mileage may vary. 1 Share this post Link to post
Guest Posted November 24, 2020 You all lost me here... I added some properties (public, not published) to TkbmMemTable, TIBOQuery and more. As long as it is the ancestor of everything else you can use interceptors / interposers. Google "Delphi interposer" Or, if you do not mind all-runtime you can simply inherit and create the objects runtime for your own flavour, lDS := TMyFDMemData.Create; But, no, if you want to add a property to TDataset it will not be added to, for example TkbmMemTable or TFDMemTable (or whatitiscalled) or any other classes inheriting from TDataset. There's no way to "intercept" even using by-the-book OOP. The TDictionary/TList approach is not bad at all, i use it a lot. Tags make me a bit nervous 🙂 Also, depending on what you are trying to achieve there are some global instance variables in Data.DB that you can reassign at application startup. For example to speed up lookups. Share this post Link to post
balabuev 102 Posted January 7, 2021 As about lifetime, TDictionary as well as TObjectDictionary do not provide sufficient functionality also. The corresponding data entries will not be removed from the dictionary when the component is destroyed. For that, FreeNotification and RemoveFreeNotification methods should be used. But, this will complicate things a lot. Share this post Link to post