If I have a class declaration like this one:
MyCollection<T: TBaseCopyable, constructor> = class
What does the keyword constructor do?
Doesn't every class have a constructor already, what is it up doing there?
It is a generic constraint.
Constraints can be associated with a type parameter of a generic. Constraints declare items that must be supported by any particular type passed to that parameter in a construction of the generic type.
This particular constraint is the constructor constraint:
A type parameter may be constrained by zero or one instance of the reserved word "constructor". This means that the actual argument type must be a class that defines a default constructor (a public parameterless constructor), so that methods within the generic type may construct instances of the argument type using the argument type's default constructor, without knowing anything about the argument type itself (no minimum base type requirements).
In a constraint declaration, you can mix "constructor" in any order with interface or class type constraints.
In my opinion it's a largely useless feature. Every time I've written a generic container class that wishes to instantiate members, I've found that I needed to be able to pass parameters to the constructor. I have absolutely no idea why the feature exists in this crippled form.
There is a well-known technique that allows generic containers to instantiate members, discussed here: Generics constructor with parameter constraint?
Related
So the following code snippet
Set mySet = {1,2,3};
is an instance of type Set which is permissible, however what would the class of the set literal be. I have tried to search for this, however I have found no answer in the dart documentation.
A literal exists only in your source code. Asking for its "class" doesn't make a lot of sense.
Using a Set, Map, or List literal is just syntactic sugar for invoking a corresponding constructor. The Set factory constructor constructs a LinkedHashSet.
However, you'll see that LinkedHashSet is also abstract. Its factory constructor returns an instance of a private, internal class. You can see its typename via print(Set().runtimeType); the actual type might be different for different platforms and is unlikely to be useful to you.
The following declaration:
type
TRec = record
constructor Create;
end;
produces this compilation error:
E2394 Parameterless constructors not allowed on record types
The documentation rather unhelpfully states:
No further information is available for this error or warning.
My question is why the language was designed this way. Was it done this way purely to echo the analogous restriction for C# structs?
The language guide says this:
Records are constructed automatically, using a default no-argument constructor, but classes must be explicitly constructed. Because records have a default no-argument constructor, any user-defined record constructor must have one or more parameters.
But that doesn't make much sense. If there is a default constructor for a record, it can't be found through RTTI. And even if there was, why would that imply that it was impossible to add another one? You can do so for classes.
Perhaps the rationale is that if we were allowed to define our own parameterless constructors, we'd expect the compiler to call them automatically.
Note: I understand that you can use a parameterless static class function as a workaround. Indeed, I personally always prefer to use static class function instead of record constructors. But that's not the point of the question. What I really want to know is why parameterless constructors are not allowed on record types.
I can't give you a definitive answer (only the compiler builders can), but I suspect it is not related to Delphi's .NET past, but rather to Delphi's relation with C++Builder.
As cppreference says:
A default constructor is a constructor which can be called with no arguments (either defined with an empty parameter list, or with default arguments provided for every parameter).
C++ allows for parameterless constructors, and these parameterless constructors would become the default constructor, in C++. A default constructor is called in many situations, e.g. if you simply declare:
Foo myFoo;
The default constructor is called. This does not happen in Delphi, but a C++ programmer might expect it. Similarly, if you do:
Foo elements[1000];
The default constructor is called on each element (I checked that). This also doesn't happen in Delphi, although a C++ programmer might expect it.
Other hints that this is C++-related:
Constructors with different names (e.g. Init) are not allowed either. This seems to point to conflicts with C++ or with C#, as in both, constructors have the name of the class or struct, so any parameterless constructor would be mapped to Foo() (in a struct or class called Foo.)
Constructors with only default parameters are not allowed either. This matches the cppreference description for default constructors with only default arguments.
All in all, there are hints that parameterless constructors (or ones with only default parameters) conflict with C++ (i.e. C++Builder) and that that is why they are not allowed.
Note that this is not the only restriction caused by differences with C++: e.g. in Delphi you can't cast integers to and from floating point types either, because in C and C++, that would cause a conversion, while in Delphi, it would merely cause a reinterpretation of the bits. In order not to confuse people who were coming to Delphi from C or C++, the casting restriction was placed on floating point types. There may be more.
How do I define a generic TList type so that I can declare a variable of that type and then assign any specialization of TList<> to it?
I want to declare this variable:
var
MyList:THowToDeclareThisListType<T>;
And then instantiate it like this:
MyList:=THowToDeclareThisListType<integer>.Create;
or
MyList:=THowToDeclareThisListType<double>.Create;
etc. I must be missing something pretty obvious here. I don't want classes, just a simple type definition.
You are trying to declare a variable like this:
var
List: TList<?>;
such that List can be assigned objects of type TList<Integer> or TList<Double> or TList<string>.
That is not possible. When you define a variable using a generic type, the type must be fully instantiated.
The only way that you can have a variable that holds any object of type TList<T> is if the variable is declared to have a common base class to TList<T>. And the common base class cannot be a non-instantiated generic. For TList<T> the only possible common base class is TObject.
So you could write
var
List: TObject;
and then assign any of your objects to List. But I'm not sure that would be terribly useful!
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.
How do I do things in a constructor in F#? I cant quite figure it out...
I would check out Constructors (F#):
Objects of class types have
constructors. There are two kinds of
constructors. One is the primary
constructor, whose parameters appear
in parentheses just after the type
name. You specify other, optional
additional constructors by using the
new keyword. Any such additional
constructors must call the primary
constructor.