Generics, Polymorphism, Interfaces: what is the solution?

I know the name is very broad - covering a lot!

And I hope this question can turn into a more extensive “information wiki thing” on topics.

What I have learned so far:

  • When using Generics, understand the concepts ( covariance and contravariance ).
  • It is NOT “wrong to use” the generic concept in conjunction with inheritance. I did it, and it could make you think about covariance problems! Make sure you “tear” the generic at the right point in your inheritance - if you combine the two.

(please correct me - if you think I'm wrong, missing or misunderstood something).

My problem:

But now I have spent countless hours trying to figure out how to solve this "big riddle" that I have on my desk. And I have some good answers from several of you SO users already, but now it's time to get something working on a larger scale.

I ventured into Generics with this: Generics and polymorphism work together

And now I'm a little stuck with this: Situations in which Generics won't work

Why am I ending covariance problems - because of my class procedure in my hierarchy.

So, I wonder if Interfaces will be my next bold step in this "saga". As one "step over" the problem of covariance. It is one thing to find out that you actually have this problem - another thing is "how to get around this."

So, if any of you are good people "there", there are any opinions on this matter - I’m all ears. Basically: Tell me to go to Interfaces (I myself have never done anything from scratch). Or ... throw me a bone in the direction you propose.

My current source pool, as indicated in the second link is above.

Here is a small snippet of my previous post that shows my covariance problem. David has kindly explained why I ran into a bush. But now I need information on how to get around this.

var aList : TBaseList<TBaseObject>; // used as a list parameter for methods aPersonList : TPersonList<TPerson>; aCustomerList : TCustomerList<TCustomer>; begin aPersonList := TPersonList<TPerson>.Create; aCustomerList := TCustomerList<TCustomer>.Create; aList := aCustomerList; <-- this FAILS !! types not equal .. end; 

Hi

+6
source share
2 answers

You cannot do what you want, but in any case you are not using generics. As Rob Kennedy said, it makes no sense to have TCustomerList<TCustomer> and TPersonList<TPerson> . The beauty of generics is that you can use the same list for different types of items. This means that the list and item type should not have any dependencies.

You can do something like:

 procedure TSomething.ProcessList<T: TBaseObject>(const aList: TBaseList<T>); begin // process the list using code that is independent of the actual type of T. end; ... var aCustomerList: TBaseList<TCustomer>; aPersonList: TBaseList<TPerson>; begin ProcessList(aCustomerList); ProcessList(aPersonList); 

You may need to specify T (some early versions of generics did not handle type inference - that is, it very well indicates the type of T from the parameter type), i.e.

  ProcessList<TCustomer>(aCustomerList); ProcessList<TPerson>(aPersonList); 

But this or something like that, this is what you should do. Nothing else makes sense, IMO. There is no need to have a variable that can contain any of these lists, for example your aList . And if you really need it, you can only use TObject , but this does not allow you to use the list in any useful way. And this is not very common.

Interfaces will not help you with this problem. You can give classes specific capabilities, that is, list items, through interfaces (another type of polymorphism). But this will not handle covariance.

+5
source

I would go for:

 TCustomCustomerList = class(TBaseList<TBaseObject>) end; TCustomerList = class(TCustomCustomerList) end; 

Whether this is acceptable in your design is a completely different matter. If the goal you are trying to achieve is to assign TCustomerList to the TBaseList variable, this will be the way to go.

0
source

Source: https://habr.com/ru/post/907926/


All Articles