Can generics really produce strongly typed collections?

B

beginwithl

hi

1) Do we consider a collection to be strongly typed only if the
insertion of an item ( that is of incompatible type ) can be
determined at compile time?

For example: we could build a non generic custom collection that
allows only items of type A to be inserted by the user.
But what if this custom collection would only check at run time
( perhaps using try-catch clause ) if inserted item is of specified
type?
Do we still call this collection strongly typed?






2) Assume class B is derived from A

If a collection implements IEnumerator<T> interface, the ‘Current’
property returns an item of type T ( or type derived from T ), and
thus casting to actual type is not needed. That is considered strongly
typed code.


a) But explicit casting is still needed if T is of type A, but foreach
variable Variab is of type B!

foreach( B Variab in our_Collection ) {…}


b) and if it happens that an item in this collection, which foreach
will try to cast to B ( in order to assign it to Variab ), is actually
of type A, an exception will occur!


Thus, I don’t see how we can claim that enumerator is strongly-typed?!






2) Next question is basically the same as previous one, but now I’m
asking about generics and strong typing in general:

Say we have a generic collection of type Some_Colection<A>, which only
stores items of type A or those derived from A. If we have classes of
type B and C ( both being derived from A ), then we may store items of
both types into some_collection<A>.

But again, chances are a user may try to do some sort of operations on
item stored in this collection, thinking it is of type B but is really
of type C and as a result an exception will be thrown. In other
words, we did manage to ensure type safety when dealing with types
not_of_type_A/not_derived_from_A, but we are still dealing with unsafe
code when it comes to operating on items ( stored inside this
collection ) derived from A?!







3)

a) Say we have a collection that implemented IEnumerable<string>. How
does foreach statement figure out that this particular collection
implemented IEnumerable<string> and not, for example, IEnumerable<int>
or whatever?


b) If a collection implements IEnumerable<string>, then object
returned by GetEnumerator() will implement two versions of a ‘Current’
member. Will in that case foreach statement always call the
IEnumerator<string> version of ‘Current’ property?


c) Since both IEnumerator<T> and IEnumerator have a member named
Current, my book suggests that we should explicitly implement
IEnumerator<T> , while IEnumerator version should be implemented in
the class itself, but I found some other sources that suggest just the
opposite. So which is correct?



thank you
 
B

beginwithl

hi Pete

No. Consider:

object obj = (object)5;
List<String> lstr = new List<String>();

lstr.Add((String)obj);

The compiler won't detect the bug at compile time. But the collection is
still (IMHO) strongly typed, and a run-time exception will be thrown
(during the cast, before the Add() method is even called).
As I see it, the term strongly typed depends on the specific code we
are considering. If we just look at definition of List<T> class, then
we can say that that piece of code ( thus class List<T> ) is strongly
typed. But when we try to decide if the following piece of code is
strongly typed :

object obj = (object)5;
List<String> lstr = new List<String>();
lstr.Add((String)obj);

then we could claim that the above code, as a whole, isn’t strongly
typed ( here I’m using the term strongly typed to refer only to
compile-time detection of a bug ).


In some sense of the phrase, it's strongly typed. I prefer "strongly
typed" to describe something that is strong both at compile and run-time,
but it's not a well-defined term, so you may find alternate definitions,
or even use alternate definitions yourself.

As long as the person with whom you're communicating agrees on the same
definition you're using, that's fine.






See above. The "foreach" statement specifically behaves in the way you
describe, and an explicit cast (even an implicit explicit cast :) )
overrides the compiler's normal type rules. Overriding the rules doesn't
mean the thing you overrode wasn't strongly typed. It means you overrode
the strong typing.

Note that due to the actual strong typing, at run-time you still get an
exception. You're not allowed to try to use something as a B if it's not
a B. That's strong typing.

Yeah, but in situation you're describing I interpret the term ‘strong
typing’ in a more general sense, meaning that the ability of run-time
to notice illegal cast is due to C# being strongly typed language, and
thus any code written in C# is in that sense strongly typed.

The only way you can get into that situation is to insert a bug in your
code, by overriding the type safety provided for you. Doing that doesn't
mean the things aren't type safe. It means that they are but you have
intentionally bypassed that type safety.

* But then we could argue that non generic collections are also type
safe, but we (users) are intentionally bypassing that type safety by
trying to cast returned item ( which is of type object ) into some
derived type, and in doing so we are risking an illegal cast.

* As I see it, by using generic collections we simply lessen the
chances that users will try to perform an illegal cast

It all depends on how you declare the interfaces and their
implementations. But, the compiler uses only the implicitly implemented
interface, assuming one exists. If IEnumerable<string> is the implicitly
implemented one, that's the one you get. If all your interface
implementations are explicit, then it will look for a generic one and if
it finds one (and _only_ one), it will use that.

Note that there are limits to what the compiler can do with "foreach". If
you implement IEnumerator implicitly and IEnumerator<string> explicitly,
then "foreach" will find the non-generic version in a "foreach" that tries
to use "int", and _it will use it_.

You mean if variable ‘Variab’ in foreach is of type 'int', but foreach
calls non generic version of 'Current', then it will try to cast an
item ( returned by IEnumerator version of 'Current' ) into 'int', and
since that returned item is actually 'string', it will throw an
exception:

foreach( int Variab in Generic_string_Collection ) {…}

You won't get an error until
run-time. Even if you implement IEnumerable<string> implicitly and
IEnumerable explicitly, if you cast your collection to IEnumerable,
"foreach" will happily use that interface for the enumeration (and again,
throw an exception at run-time, since in reality the collection isn't a
collection of ints).

By casting my collection to IEnumerable you mean:

foreach( int Variab in ( IEnumerable )Some_Colection ) {…}

then foreach will of course call IEnumerable version of 'Current'
property?


Finally note that if you attempt to implement multiple generic
IEnumerable<T> interfaces on a class, the compiler will require that you
specifically cast your collection to the interface you intend to use.

You mean I will have to do the following:

foreach( int Variab in ( IEnumerable<some_type> ) Some_Colection ) {…}



thanx mate
 
B

beginwithl

hi Pete
[...]
The only way you can get into that situation is to insert a bug in your
code, by overriding the type safety provided for you.  Doing that  
doesn't
mean the things aren't type safe.  It means that they are but you have
intentionally bypassed that type safety.
* But then we could argue that non generic collections are also type
safe, but we (users) are intentionally bypassing that type safety by
trying to cast returned item ( which is of type object ) into some
derived type, and in doing so we are risking an illegal cast.

You could argue anything you want.

But, I would disagree with that position.  Most non-generic collections 
are type-safe only in a degenerate sense.  That is, they are type-safe for  
collections of type "Object", and only for collections of type "Object".  
If "Object" is the only aspect of the type you care about, then  
sure...those collections are type-safe.

But I find that use of the phrase "type-safe" useless, since everything in  
.NET inherits Object.

Note that there are non-generic collections that have run-time type  
safety, even though they aren't compile-time type-safe.

You mean there are non generic collections that also allow only
certain types of items to be inserted, and thus throw an exception
when item is not of that type?

Generics _eliminate_ the chances that users will put something into the  
collection that doesn't match the type of the collection as declared.  
This is quite a bit different from the guarantee that non-generic,  
non-type-safe collections offer.

Users can illegally cast anything any time they want.  But with a generic,  
type-safe collection, we can at least be sure that any such illegal casts 
cannot occur with objects found in the collection itself.

I didn’t quite get what you meant by “that any such illegal casts
cannot occur with objects found in the collection itself.” .

Since I think we both agree that if we have a collection of type
Some_Colection<A>, then only items of that type or derived from A can
be inserted into this collection. And if B and C are both derived from
A, then user could perform an illegal cast on item of type C, due to
thinking it is actually of type B. As such, I’m gonna assume you meant
something else by that statement:

Thus, you’re probably saying that inside a collection there could be
particular method operating in some way on items in this collection,
and if this collection was instead non generic, but method ( declared
inside this non generic collection ) non the less assumed that stored
items were only of type A, then illegal cast could happen if some user
stored into this collection an item of type object or int or
whatever?!

thank you


[...]
Note that there are limits to what the compiler can do with "foreach". 
If
you implement IEnumerator implicitly and IEnumerator<string> explicitly,
then "foreach" will find the non-generic version in a "foreach" that  
tries
to use "int", and _it will use it_.
You mean if variable ‘Variab’ in foreach is of type 'int', but foreach
calls non generic version of 'Current', then it will try to cast an
item ( returned by IEnumerator version of 'Current' ) into 'int', and
since that returned item is actually 'string', it will throw an
exception:
foreach( int Variab in Generic_string_Collection ) {…}
Yes
By casting my collection to IEnumerable you mean:
foreach( int Variab in ( IEnumerable )Some_Colection ) {…}
then foreach will of course call IEnumerable version of 'Current'
property?
Yes.
You mean I will have to do the following:
foreach( int Variab in ( IEnumerable<some_type> ) Some_Colection ) {…}

And yes.  :)

Pete
 
B

beginwithl

Sorry...I didn't think I needed to be more explicit than that in my  
explanation.

You can obviously attempt to cast any reference to any type you like.  
Nothing any language or framework can do could possibly prevent you from  
doing that.

Your example of a "Some_Colection<A>" where you try to cast a B to a C  
instead is overly restrictive.  You could try to cast an object in the  
collection to any type you like.  String, FileStream, Control, whatever..  
If it's not that type, the cast will fail, no matter where you got the  
reference, and regardless of whether the correct type shares a base class 
with the incorrect type.

The point is that if you have a generic collection of a given type, then  
you can always use references from the collection as that given type  
without worrying about a cast failing.  It simply cannot happen, because  
the collection can never have something in it of the wrong type.

So if users are worried about illegal casts, their only option is to
just use references of exact same type as all items in collection
should be ( in case of Some_Colection<A> users should make a rule to
always use references of type A )?

So do we consider it to be a good design if user code always assumes
that items in Some_Colection<A> are always of type A ( and not of
some type derived from A ), and thus always threats those items only
as being of type A. And if at any time user code wants to also operate
on some item of type B ( which is derived from A ), then it should
instead create/use a new collection Some_Colection<B>?



BTW - It is argued that unlike other generic interfaces,
IEnumerable<T> ( and thus IEnumerator<T> ) can inherit from their
non generic counterparts because strong typing is not defeated, and
that is due to IEnumerable<T> being contra-variant: In IEnumerable<T>,
the type parameter T is used only in "output" positions (return
values) and not in "input" positions (parameters). Thus strong typing
is not defeated if Current also returns an instance of type object.

But I would argue that is wrong, since I see a generic type as a
contract of sort, contract being that when asked, it will only return
to the user items of “promised” type ( in case of IEnumerable<string>
and IEnumerator<string>, items of type string ). I know we argued that
it’s up to user to make sure casts are valid, but still …


cheers mate
 
P

Pavel Minaev

So if users are worried about illegal casts, their only option is to
just use references of exact same type as all items in collection
should be ( in case of Some_Colection<A> users should make a rule to
always use references of type A  )?

If users are worried about illegal casts, they shouldn't use explicit
casts.

Explicit downcasts are by their nature dangerous. If the compiler
would know that the cast is always successful, there wouldn't be a
need for an explicit one. Its sole purpose is to override the static
type checking, but then you take responsibility to ensure that the
cast is correct.
So do we consider it to be a good design if user code always assumes
that items in Some_Colection<A>  are always of type A ( and not of
some type derived from A ), and thus always threats those items only
as being of type A.

Generally, yes. Lack of need for explicit downcasts is a sign of good
OO design.
And if at any time user code wants to also operate
on some item of type B ( which is derived from A ), then it should
instead create/use a new collection  Some_Colection<B>?

Not really, it just shouldn't get that item from the collection that
is of element type A. Or it should use "is" & "as" (and wrappers, such
as Enumerable.OfType()) to filter out objects of desired type.
BTW - It is argued that unlike other generic interfaces,
IEnumerable<T> ( and thus IEnumerator<T> )  can inherit from their
non  generic counterparts because strong typing is not defeated, and
that is due to IEnumerable<T> being contra-variant: In IEnumerable<T>,
the type parameter T is used only in "output" positions (return
values) and not in "input" positions (parameters). Thus strong typing
is not defeated if Current also returns an instance of type object.

But I would argue that is wrong, since I see a generic type as a
contract of sort, contract being that when asked, it will only return
to the user items of “promised” type ( in case of IEnumerable<string>
and IEnumerator<string>, items of type string ). I know we argued that
it’s up to user to make sure casts are valid, but still …

IEnumerable<T> honors this contract by requiring that
IEnumerable.Current == IEnumerable<T>.Current for any implementation.
This isn't expressible in C# code, but it's part of the contract
nonetheless.
 
B

beginwithl

hi


IEnumerable<T> honors this contract by requiring that
IEnumerable.Current == IEnumerable<T>.Current for any implementation.
This isn't expressible in C# code, but it's part of the contract
nonetheless.

I’m not sure I understand in what way should IEnumerable.Current equal
IEnumerable<T>.Current?!

Afterall, IEnumerable.Current returns an item of type object, not T?!

Unless you are implying that if we implement IEnumerable<string>, then
we should make sure that IEnumerable.Current will also return only
items of type string ( which Current returns as items of type
object )?


This isn't expressible in C# code, but it's part of the contract
nonetheless.

You mean even though we can't express that contract in C#, run-time or
compiler or whatever makes sure that contract is not broken? Uh!


BTW – why did you specify IEnumerable.Current and not
IEnumerator.Current? Is it just a shorthand notation for the fact that
object implementing IEnumerable will return IEnumerator object, the
member of which is Current?



cheers
 
P

Pavel Minaev

I’m not sure I understand in what way should IEnumerable.Current equal
IEnumerable<T>.Current?!

Afterall, IEnumerable.Current returns an item of type object, not T?!

It does, but even so. For any T which is a reference time, the same
enumerator object has to have IEnumerator.Current ==
IEnumerator<T>.Current. And for any value type, it should be
Object.Equals(IEnumerator.Current, IEnumerator<T>.Current). In other
words, part of the contract of the interface is that
IEnumerator.Current returns an object of type T cast (and boxed, if
needed) to object.
Unless you are implying that if we implement IEnumerable<string>, then
we should make sure that IEnumerable.Current will also return only
items of type string ( which Current returns as items of type
object )?
Precisely.

You mean even though we can't express that contract in C#, run-time or
compiler or whatever makes sure that contract is not broken? Uh!

Well, yes. You need full-fledged Design by Contract support - as in
e.g. Eiffel - to do that sort of thing. In this case it'd probably be
a class invariant.
BTW – why did you specify IEnumerable.Current and not
IEnumerator.Current? Is it just a shorthand notation for the fact that
object implementing IEnumerable will return IEnumerator object, the
member of which is Current?

Actually, it was just a mix-up. Yes, of course it's
IEnumerator.Current.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top