Accessing members of a generic type - delphi

Lets say I have 2 class types TEmployee (with properties A,B) and TDept (with properties C,D). Then I make a class descended from TList like so :
TMyCcontainer<T>=class(TList<T>)
So I can create instances of TMyCcontainer and fill with TEmployee or TDept. In my TMyCcontainer class is there anyway to access properties A,B of TEmployee, or properties C,D of TDept?
Of course the type is generic so it would appear not. And this is the problem I always have with generics - maybe I am mis-using them. I recently learnt abaout constraints, and thought I had found out what I had been missing....
So I created 2 interfaces say IEmployee and IDept, made my 2 orig class es to be interfaceobjects, and put in my contraint on my Tlist ie
TMyCcontainer<T:IEmployee,IDept>=class(TList<T>)
Of course I was quickly disappointed as this is saying you must implement BOTH of these interfaces in any type I put in my generic TList (TMyContainer), whereas I just want ONE in any particular instance, then ther other in another instance. I would have to implement both IEmployee and IDept in my TDept class which is not what I want obv.
Is there any good way to access members of a Type within a generic container? Or should I not be using generics to do this type of thing. Ty

All generic constraints you put on a class have to be fulfilled by the generic type. Looks like what you're really looking for is two different generic types: TMyContainer<TDept> and TMyContainer<TEmployee>. Then you'll have access to all the properties of those types, individually.

Related

Is there a modifiable Iterable type?

I need a modifiable collection like a List or a Set to be passed as a parameter. Using Iterable doesn't guarantee this argument to have methods like add or remove.
Example method:
void foo(Iterable bar) {
bar.add(); // The method 'add' isn't defined for the type 'Iterable'.
}
Is there a class / interface for (modifiable) collections which guarantees those methods? If not, why?
There is not a modifiable type. Very early (before Dart 1) we had some other types in our hierarchy, but we decided to avoid including them because things were getting a bit too complex.
I still wish we'd shipped a List interface without the mutation members. 🤷

Delphi Polymorphism using a "sibling" class type

We have an app that makes fairly extensive use of TIniFile. In the past we created our own descendant class, let's call it TMyIniFile, that overrides WriteString. We create one instance of this that the entire app uses. That instance is passed all around through properties and parameters, but the type of all of these is still TIniFile, since that is what it was originally. This seems to work, calling our overridden method through polymorphism, even though all the variable types are still TIniFile. This seems to be proper since we descend from TIniFile.
Now we are making some changes where we want to switch TMyIniFile to descend from TMemIniFile instead of TIniFile. Those are both descendants of TCustomIniFile. We'll also probably be overriding some more methods. I'm inclined to leave all the declarations as TIniFile even though technically our class is no longer a descendant of it, just to avoid having to change a lot of source files if I don't need to.
In every tutorial example of polymorphism, the variable is declared as the base class, and an instance is created of the descendant class and assigned to the variable of the base class. So I assume this is the "right" way to do it. What I'm looking at doing now will end up having the variables declared as, what I guess you'd call a "sibling" class, so this "seems wrong". Is this a bad thing to do? Am I asking for trouble, or does polymorphism actually allow for this sort of thing?
TIniFile and TMemIniFile are distinct classes that do not derive from each other, so you simply cannot create a TMemIniFile object and assign it to a TIniFile variable, and vice versa. The compiler won't let you do that. And using a type-cast to force it will be dangerous.
You will just have to update the rest of your code to change all of the TIniFile declarations to TCustomIniFile instead, which is the common ancestor for both classes. That is the "correct" thing to do.
The compiler is your friend - why would you lie to it by using the wrong type ... and if you do lie to it why would you expect it to know what you want it to do?
You should use a base class that you derive from, like TCustomIniFile. I would expect compile issues if you are trying to make assignments which are known at compile time to be wrong.
The different classes have different signatures so the compiler needs to know which class it is using to call the correct method or access the correct property. With virtual methods the different classes setup their own implementation of those methods so that the correct one is called - so using a pointer to a base type when you call the virtual method it calls that method in the derived type because it is in the class vtable.
So if the code does compile, it's very likely that the compiler will not be doing the right thing ...

Why should i use TCollections.CreateList<T> and not TList<T>.Create

i added map(), reduce() and where(qlint : string) to a Spring4D fork of mine.
While i was programming these functions, i found out that there is a differnce in the behaviour of the lists, when they are created in different ways.
If i create them with TList<TSomeClass>.create the objects in the enumerables are of the type TSomeClass.
If i create them with TCollections.CreateList<TSomeClass> the objects in the enumerables are of the type TObject.
So the question is:
Is there a downside by using TList<TSomeClass>.create ?
Or in other words: Why should i use TCollections.CreateList<TSomeClass> ?
btw: with TCollections.CreateList i got a TObjectList and not a TList. So it should be called TCollections.CreateObjectList... but that's another story.
Depending on the compiler version many of the Spring.Collections.TCollections.Create methods are applying what the compiler is unable to: folding the implementation into only a very slim generic class. Some methods are doing that from XE on, some only since XE7 ( GetTypeKind intrinsic function makes it possible to do the type resolution at compile time - see the parameterless TCollections.CreateList<T> for example).
This greatly reduces the binary size if you are creating many different types of IList<T> (where T are classes or interfaces) because it folds them into TFolded(Object|Interface)List<T>. However via the interface you are accessing the items as what you specified them and also the ElementType property returns the correct type and not only TObject or IInterface. On Berlin it adds less than 1K for every different object list while it would add around 80K if the folding is not applied due to all the internal classes involved for the different operations you can call on an IList<T>.
As for TCollections.CreateList<T> returning an IList<T> that is backed by a TFoldedObjectList<T> when T is a class that is completely as designed. Since the OwnsObject was passed as False it has the exact same behavior as a TList<T>.
The Spring4D collections are interface based so it does not matter what class is behind an interface as long as it behaves accordingly to the contract of the interface.
Make sure that you only carry the lists around as IList<T> and not TList<T> - you can create them both ways (with the benefits I mentioned before when using the TCollections methods). In our own application some places are still using the constructor of the classes while many other places are using the static methods from Spring.Collections.TCollections.
BTW:
I saw the activity in your fork and imo there is no need to implement Map/Reduce because that is already there. Since the Spring4D collections are modelled after .NET they are called Select and Aggregate (see Spring.Collections.TEnumerable). They are not available on IEnumerable<T> directly though because interfaces must not have generic parameterized methods.

Constructor in an Interface

I know - I cannot, but.
I want all classes who will implement my interface to have the same owner (usually defined in constructor). What is the best practice to do that?
Should I use some base abstract class or something like this?
An interface defines a contract between implementor and consumer.
Part of that contract is enforced by the compiler. For example, that all implementations of the interface have the requisite functions of particular names, which take specific parameters.
But there is another part of an interface that is not enforced by the compiler. That's the part of the contract that is described in the interface documentation. You could decide that it suffices to tell all implementors what rules they must abide by. Many libraries take that stance. The Windows API is one prominent example.
If you are dead set on enforcement through code then an interface cannot help. You need something that expresses the constraint in code and in this case that's going to require implementation. Which means you would need to use a class. An (almost) abstract base class could get it done. The only concrete part of the class would enforce the ownership constraint. The rest of the class would be a series of abstract virtual methods. That's not an interface in the sense implied by the Delphi keyword. However, it's an interface in semantic terms.
Of course you can choose to implement the interface in a common ancestor, or at least have a common ancestor for the classes that implement the interface. However, you cannot enforce this through the interface. An interface has no constructor and an interface cannot enforce which class can or cannot implement it.
I think the best option would be to expose an Owner property through the interface. That way, you can at least get the owner through the interface, and you will enforce the implementing classes to at least implement that property. B.t.w, an interface is allowed to have properties and methods that return objects or have object parameters.

Delphi - Accessing Object Instance Data from Another Object

I have my main form. Form_Main
It creates two instances of two classes.
Candle_Data : TCandle_Data;
Indicator_2700 : TIndicator_2700;
In order for Indicator_2700 to properly compute its values it must have access to the candle data in the obect Candle_Data from inside one of its methods. Thus how can Indicator_2700 access data inside Candle_Data? Does Form_Main have to pass it as a argument at Constructor time?
Both Class declarations are in their own unit file.
You could use any of the following (non-exhaustive) methods:
Pass the object reference as a parameter to any methods that need it. Of course you need to get hold of Candle_Data so the suitability of this approach really depends who the caller is.
Pass the Candle_Data object reference to the constructor of the other object and then store it in a private member field.
Make the object reference a public property of the single instance of the main form and access it that way.
We don't really have enough information to advise you which is best but the starting point is always to prefer parameters and local variables over global state.
TIndicator_2700 could have a property to link it to the instance of TCandle_Data that is relevant to its own instance or you should supply it as an argument to the method that needs to access the data.
You could certainly pass the TCandle_Data instance into the constructor of Indicator_2700, and store a reference within the resulting instance until you needed it.
Both class declarations are in their own unit file.
That suggests that both have nothing to do with the other. But still you want one to have knowledge about the other. It sounds like a little design mixup, but that doesn't need to be the case.
There are multiple solutions, here are three of them, each with its own purpose:
Place both classes in the same unit, only if both classes have a common theme/subject (e.g. TCar and TAirplane in the unit Transport),
Use one unit in the other unit, only if both units represent different subjects, but one may depend on the other (e.g. unit Transport uses unit Fuel: TCar needs TDiesel, but TDiesel doesn't need a TCar). This only works one-way. Delphi prevents using in both ways with a compiler error: "Circular unit reference to 'Fuel'". The only workaround is to use the second unit in the implementation section, but that usually is considered a big nono.
Declare a new base-class in a new unit, only if the base-class has a common subject, but the final descendants do not (e.g. TFuel is used by all transportation classes like TCar, TAirplane and THorse, but TFood (a descendant of TFuel) is only used by THorse and TPerson).
As for how to link both classes together, see the already given answers.

Resources