473,469 Members | 1,512 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

Question about Generics

Hello,

I'm wondering if it's possible to do the following with Generics:

Let's say I have a generic member variable as part of a generic class
like this:

List<DLinqQuery<TDataContext>> _queries;

where DLinqQuery is a generic class that takes a type parameter
TDataContext.

This member variable is part of a generic class called
DLinqDataSource<TDataContext> and is exposed as a public property of
the specified type.

It is assumed that all TDataContext types will be derived from the
concrete class DataContext.

Now, let's say I derive a class from DataContext called MyDataContext.

I the create two concrete classes like this:

public class MyDataQuery : DLinqQuery<MyDataContext>{}
public class MyDataSource : DLinqDataSource<MyDataContext>{}

I want to be able to override the property in the MyDataSource class to
return a List<MyDataQuery> instead of the generic
List<DLinqQuery<TDataContext>> which would become
List<DLinqQuery<MyDataContext> in an instantiation of MyDataSource.

Unfortunately, casting the base property value (which is a
List<DLinqQuery<MyDataContext>> to the type List<MyDataQuery> just
doesn't work, but from a functional perspective it seems like that it
would.

Any ideas on whether this can be done?

Jun 30 '06 #1
11 2443
The reason you can not do this is because generic types are not
covariant (I believe that is the term). This means that if you have a type
Dog which derives from Animal, and a List<Dog>, you can not cast it to a
List<Animal>.

The reason for this is that when you have a class, Cat which derives
from Animal, and you have the new List<Animal> (cast from List<Dog>), that
list wouldn't know what to do with the Cat when it is added.

The workaround here is to define your classes like so:

public class MyDataQuery : DLinqQuery<MyDataContext>{}

public class MyDataSource<T> : DLinqDataSource<T>{}

Then, when you declare your MyDataSource, you would do this:

MyDataSource<MyDataQuery> ds = new MyDataSource<MyDataQuery>();

Hope this helps.
--
- Nicholas Paldino [.NET/C# MVP]
- mv*@spam.guard.caspershouse.com
"ha****************@gmail.com" <ha*********@gmail.com> wrote in message
news:11**********************@y41g2000cwy.googlegr oups.com...
Hello,

I'm wondering if it's possible to do the following with Generics:

Let's say I have a generic member variable as part of a generic class
like this:

List<DLinqQuery<TDataContext>> _queries;

where DLinqQuery is a generic class that takes a type parameter
TDataContext.

This member variable is part of a generic class called
DLinqDataSource<TDataContext> and is exposed as a public property of
the specified type.

It is assumed that all TDataContext types will be derived from the
concrete class DataContext.

Now, let's say I derive a class from DataContext called MyDataContext.

I the create two concrete classes like this:

public class MyDataQuery : DLinqQuery<MyDataContext>{}
public class MyDataSource : DLinqDataSource<MyDataContext>{}

I want to be able to override the property in the MyDataSource class to
return a List<MyDataQuery> instead of the generic
List<DLinqQuery<TDataContext>> which would become
List<DLinqQuery<MyDataContext> in an instantiation of MyDataSource.

Unfortunately, casting the base property value (which is a
List<DLinqQuery<MyDataContext>> to the type List<MyDataQuery> just
doesn't work, but from a functional perspective it seems like that it
would.

Any ideas on whether this can be done?

Jun 30 '06 #2
ha****************@gmail.com wrote:
Hello,

I'm wondering if it's possible to do the following with Generics:

Let's say I have a generic member variable as part of a generic class
like this:

List<DLinqQuery<TDataContext>> _queries;

where DLinqQuery is a generic class that takes a type parameter
TDataContext.

This member variable is part of a generic class called
DLinqDataSource<TDataContext> and is exposed as a public property of
the specified type.

It is assumed that all TDataContext types will be derived from the
concrete class DataContext.

Now, let's say I derive a class from DataContext called MyDataContext.

I the create two concrete classes like this:

public class MyDataQuery : DLinqQuery<MyDataContext>{}
public class MyDataSource : DLinqDataSource<MyDataContext>{}

I want to be able to override the property in the MyDataSource class to
return a List<MyDataQuery> instead of the generic
List<DLinqQuery<TDataContext>> which would become
List<DLinqQuery<MyDataContext> in an instantiation of MyDataSource.

Unfortunately, casting the base property value (which is a
List<DLinqQuery<MyDataContext>> to the type List<MyDataQuery> just
doesn't work, but from a functional perspective it seems like that it
would.

Any ideas on whether this can be done?


Hi,

This question has been asked a few times here. The answer I gave to one of
them was that you cannot cast it, because a List<A> is not, and is not a
derivative of a List<B> if A derives from B. They're two different types,
because the type is expressed as a result of the type name and it's type
parameters.

So, you'll have to write a function that will convert the
List<DLinqQuery<MyDataContext>> to List<MyDataQuery>. Also, the definition
of override is to change the behaviour of a virtual method defined in a
base class, not to alter it's signature, e.g.:

public virtual bool foo ( int bar ) { ... }

Can only be overriden to change the behaviour of 'foo', not it's return
type, and not it's parameters.

The generic List class contains a generic method called ConvertAll<> which
can help you convert the list, or you can just enumerate through your list
and build a brand new one.

--
Hope this helps,
Tom Spink
Jun 30 '06 #3
Nicholas Paldino [.NET/C# MVP] <mv*@spam.guard.caspershouse.comwrote:
The reason you can not do this is because generic types are not
covariant (I believe that is the term).
It is - although the CLR itself supports covariance. You can tell the
CLR that something is a list of "some type which is derived from Dog"
or "some type which is a super-type of Dog". However, C# doesn't expose
this, and neither do the framework libraries.

It's a bit of a shame - the Java libraries *do* expose this, and
although it's a fairly confusing topic, it can be very handy. With Mads
on the C# team now, I wouldn't be surprised to see
covariance/contravariance appear in a later version - although now the
standard libraries have been defined without support, the usefulness of
them would be very much diminished.

(I should say that this is one of the very few places where Java
generics are better than C# generics.)

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jul 2 '06 #4
Jon Skeet [C# MVP] wrote:
Nicholas Paldino [.NET/C# MVP] <mv*@spam.guard.caspershouse.comwrote:
> The reason you can not do this is because generic types are not
covariant (I believe that is the term).

It is - although the CLR itself supports covariance. You can tell the
CLR that something is a list of "some type which is derived from Dog"
or "some type which is a super-type of Dog". However, C# doesn't expose
this, and neither do the framework libraries.

It's a bit of a shame - the Java libraries *do* expose this, and
although it's a fairly confusing topic, it can be very handy. With Mads
on the C# team now, I wouldn't be surprised to see
covariance/contravariance appear in a later version - although now the
standard libraries have been defined without support, the usefulness of
them would be very much diminished.
Hi Jon,
(I should say that this is one of the very few places where Java
generics are better than C# generics.)
I agree with that, very reluctantly. I've always found Java generics nasty,
since it's not a feature of the JVM, but a feature of the compiler,
unlike .NET which is a feature of the CLI, not the compiler, which IMHO,
the latter feels nicer. <g>

--
Hope this helps,
Tom Spink
Jul 2 '06 #5
Tom Spink <ts****@gmail.comwrote:
(I should say that this is one of the very few places where Java
generics are better than C# generics.)

I agree with that, very reluctantly. I've always found Java generics nasty,
since it's not a feature of the JVM, but a feature of the compiler,
unlike .NET which is a feature of the CLI, not the compiler, which IMHO,
the latter feels nicer. <g>
It's certainly better to be part of the platform itself (although it's
still a feature of the compiler, of course. If the compiler doesn't
support generics, you're not going to be able to use them). I'd say
it's still nicer to have Java-style generics than not to have them at
all, but it would have been much better to have .NET-style generics,
even without covariance.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jul 2 '06 #6
Jon Skeet [C# MVP] <sk***@pobox.comwrote:
Nicholas Paldino [.NET/C# MVP] <mv*@spam.guard.caspershouse.comwrote:
The reason you can not do this is because generic types are not
covariant (I believe that is the term).

It is - although the CLR itself supports covariance. You can tell the
CLR that something is a list of "some type which is derived from Dog"
or "some type which is a super-type of Dog". However, C# doesn't expose
this, and neither do the framework libraries.

It's a bit of a shame - the Java libraries *do* expose this, and
although it's a fairly confusing topic, it can be very handy. With Mads
on the C# team now, I wouldn't be surprised to see
covariance/contravariance appear in a later version - although now the
standard libraries have been defined without support, the usefulness of
them would be very much diminished.

(I should say that this is one of the very few places where Java
generics are better than C# generics.)
A problem with enabling covariance and contravariance is that it would
add type-checks at the CLR level to operations which currently don't
need a type-check. For example, covariance of object arrays is enabled
in C#. As a result, all writes into an object array cause a runtime
type-check.

The type-safe way to enable covariance and contravariance is to limit
the methods callable on a generic parameter explicitly allowed to accept
covariant or contravariant values. Basically, variables of a covariant
type are allowed to return the covariant type in out or return
parameters only, while variables of a contravariant type are allowed to
accept values of a contravariant types only.

There is good coverage of this issue in a recent MS paper:

Variance and Generalized Constraints for C# Generics
Burak Emir, Andrew J. Kennedy, Claudio Russo, Dachuan Yu
July 2006

http://research.microsoft.com/resear...edings&id=1215

Direct link:
http://research.microsoft.com/~akenn...cs/ECOOP06.pdf

Discussion on Lambda the Ultimate:

http://lambda-the-ultimate.org/node/1573

-- Barry

--
http://barrkel.blogspot.com/
Jul 3 '06 #7
Barry Kelly <ba***********@gmail.comwrote:
(I should say that this is one of the very few places where Java
generics are better than C# generics.)

A problem with enabling covariance and contravariance is that it would
add type-checks at the CLR level to operations which currently don't
need a type-check. For example, covariance of object arrays is enabled
in C#. As a result, all writes into an object array cause a runtime
type-check.
That's one sort of covariance, yes.
The type-safe way to enable covariance and contravariance is to limit
the methods callable on a generic parameter explicitly allowed to accept
covariant or contravariant values. Basically, variables of a covariant
type are allowed to return the covariant type in out or return
parameters only, while variables of a contravariant type are allowed to
accept values of a contravariant types only.
That's the sort of covariance/contravariance I was thinking of :)
Perhaps there ought to be two different terms for it...

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Jul 3 '06 #8
Jon Skeet [C# MVP] <sk***@pobox.comwrote:
Barry Kelly <ba***********@gmail.comwrote:
(I should say that this is one of the very few places where Java
generics are better than C# generics.)
A problem with enabling covariance and contravariance is that it would
add type-checks at the CLR level to operations which currently don't
need a type-check. For example, covariance of object arrays is enabled
in C#. As a result, all writes into an object array cause a runtime
type-check.

That's one sort of covariance, yes.
The type-safe way to enable covariance and contravariance is to limit
the methods callable on a generic parameter explicitly allowed to accept
covariant or contravariant values. Basically, variables of a covariant
type are allowed to return the covariant type in out or return
parameters only, while variables of a contravariant type are allowed to
accept values of a contravariant types only.

That's the sort of covariance/contravariance I was thinking of :)
Perhaps there ought to be two different terms for it...
If one describes reading from an array as a method "T GetValue(int)",
and writing into an array as "void SetValue(int, T)", then there is an
exact correspondence between covariance/contravariance in classes and
covariance in arrays, as described above. They're the same thing. The
SetValue(int, T) method should be contravariant, not covariant - e.g.
considering arrays of dogs and mammals, if SetValue() is going to be the
only "method" called on the array, then one should be able to supply an
array of mammals where an array of dogs is expected. Slightly
counter-intuitive, perhaps, but that's where the "contra" comes in :)

The paper gives good coverage of a possible way of introducing it into
C#. My only concern is that it would confuse things, since reasoning
about covariance / contravariance can be a little tricky.

-- Barry

--
http://barrkel.blogspot.com/
Jul 3 '06 #9
Barry Kelly wrote:
That's the sort of covariance/contravariance I was thinking of :)
Perhaps there ought to be two different terms for it...

If one describes reading from an array as a method "T GetValue(int)",
and writing into an array as "void SetValue(int, T)", then there is an
exact correspondence between covariance/contravariance in classes and
covariance in arrays, as described above. They're the same thing.
Not quite (IMO). The CLR supports covariance/contravariance by allowing
a type to be declared as List<Stream+or List<Stream-for instance.
That is a sort of *declarative* covariance/contravariance which can
tell the compiler that it *will* support one of GetValue or SetValue
(and which one :) That's not the same as the runtime
covariance/contravariance of arrays in terms of compile-time type
safety, so it's worth making the distinction between them. The CLR
supports compile-time-unsafe covariance for arrays, but
compile-time-safe covariance/contravariance for generics. C# supports
compile-time-unsafe covariance for arrays, but has no concept of the
compile-time-safe generic covariance/contravariance supported by the
CLR.

Does that explain the distinction I make between the two sorts of
covariance any better?

Jon

Jul 3 '06 #10
"Jon Skeet [C# MVP]" <sk***@pobox.comwrote:
Barry Kelly wrote:
That's the sort of covariance/contravariance I was thinking of :)
Perhaps there ought to be two different terms for it...
If one describes reading from an array as a method "T GetValue(int)",
and writing into an array as "void SetValue(int, T)", then there is an
exact correspondence between covariance/contravariance in classes and
covariance in arrays, as described above. They're the same thing.

Not quite (IMO). The CLR supports covariance/contravariance by allowing
a type to be declared as List<Stream+or List<Stream-for instance.
Let's be clear in exactly what the CLR does and doesn't support, within
the spectrum of variance. I'll try and enumerate the different
possibilities, as far as I know them:

1) Covariance using inheritance, for return values / out-parameters (or
contravariance using inheritance, for in-arguments). This includes the
covariance supported by C++. If it were valid in C#, one could declare:

class Mammal
{
public virtual Mammal GetValue()
{
Console.WriteLine("Mammal::GetValue");
return null;
}
}

class Dog : Mammal
{
public override Dog GetValue()
{
Console.WriteLine("Dog::GetValue");
return null;
}
}

This will get an error in C#. It isn't supported in IL, because the
return type is part of the method specification, and there's no
"override" keyword in IL. Thus, the IL translation of the above would
define "Dog::GetValue" as a new, overloaded virtual method. Finally,
even though native C++ supports the construct, managed C++ does not. It
gives this error message:

---8<---
error C2392: 'Dog ^Dog::GetValue(void)' : covariant returns types are
not supported in managed types, otherwise 'Mammal
^Mammal::GetValue(void)' would be overridden
--->8---

2) Covariance / contravariance of generic parameters. The CLI spec (II
9.5) says that covariance and contravariance is supported for interfaces
and delegate types, but not for class or value types. The CLI support is
at the definition site, not the use site. The PDF I linked to doesn't
talk about the existing CLI support, but rather about a different kind
of variance - use-site variance. The CLI support is for definition-site
variance. This is different to Java 5 variance support, which supports
use-site variance.

So, neither List<-Streamnor List<+Streamcan be declared. Instead,
the declaration would need to look like List<+Tor List<-T>. And even
then, List<would need to be an interface, and what's more, the
covariant definition can't accept T values as input arguments for any of
its methods, and similarly the contravariant can't return T values.

---8<---
class Mammal { }
class Dog : Mammal { }

interface IReader<+T// allows covariance
{
T GetValue();
}

interface IWriter<-T// allows contravariance
{
void SetValue(T value);
}
--->8---

2a) Covariance of generic parameters.

This would allow the following, using the CIL syntax for covariance:

IReader<DogdogReader = null;
IReader<MammalmammalReader = dogReader;

2b) Contravariance of generic parameters.

This would allow:

IWriter<MammalmammalWriter = null;
IWriter<DogdogWriter = mammalWriter;

These are both disallowed in C#, but allowed at the IL level. Here's a
test IL file which assembles and passes PEVerify:

---8<---
..assembly extern mscorlib {}
..assembly Test {}

..class private auto ansi beforefieldinit Mammal
extends [mscorlib]System.Object {}

..class private auto ansi beforefieldinit Dog
extends Mammal {}

..class interface private abstract auto ansi IReader`1<+T>
{
.method public hidebysig newslot abstract virtual
instance !T GetValue() cil managed {}
}

..class interface private abstract auto ansi IWriter`1<-T>
{
.method public hidebysig newslot abstract virtual
instance void SetValue(!T 'value') cil managed {}
}

..class private auto ansi beforefieldinit App
extends [mscorlib]System.Object
{
.method private hidebysig static void Main() cil managed
{
.entrypoint
.locals init (
[0] class IReader`1<class DogdogReader,
[1] class IReader`1<class MammalmammalReader,
[2] class IWriter`1<class MammalmammalWriter,
[3] class IWriter`1<class DogdogWriter)

ldnull
stloc.0

ldloc.0
stloc.1

ldnull
stloc.2

ldloc.2
stloc.3

ret
}
}
--->8---

If one switches around the assignments, to try and treat covariance
contravariantly and vice versa, changing the body of the Main method to:

---8<---
ldnull
stloc.1

ldloc.1
stloc.0

ldnull
stloc.3

ldloc.3
stloc.2

ret
--->8----

One then gets the following errors from PEVerify:

---8<---
[IL]: Error: [App::Main][found ref 'IReader`1[Mammal]'][expected ref
'IReader`1[Dog]'] Unexpected type on the stack.
[IL]: Error: [App::Main][found ref 'IWriter`1[Dog]'][expected ref
'IWriter`1[Mammal]'] Unexpected type on the stack.
--->8---

Similarly, if one tries to make IReader<+Tcontravariant, i.e. change
to IReader<-T>, and IWriter<-Tcovariant, one gets the following
errors:

---8<---
[token 0x02000004] Type load failed.
[token 0x02000005] Type load failed.
--->8---

So, the covariant and contravariant support is there.

3) Covariance of arrays, the way C# and Java supports it.

This acts more or less like T[] was defined "Array<+T>", with the
addition of a run-time check for input arguments, which can only support
contravariance, not covariance.
That is a sort of *declarative* covariance/contravariance which can
tell the compiler that it *will* support one of GetValue or SetValue
(and which one :) That's not the same as the runtime
covariance/contravariance of arrays in terms of compile-time type
safety, so it's worth making the distinction between them. The CLR
supports compile-time-unsafe covariance for arrays, but
compile-time-safe covariance/contravariance for generics. C# supports
compile-time-unsafe covariance for arrays, but has no concept of the
compile-time-safe generic covariance/contravariance supported by the
CLR.

Does that explain the distinction I make between the two sorts of
covariance any better?
It does, in so far as array covariance isn't provably sound - instead it
relies on runtime type checks. That's one clear demarcation.

-- Barry

--
http://barrkel.blogspot.com/
Jul 3 '06 #11
Barry Kelly wrote:

<snip>

Thanks for that. I hadn't realised that only interfaces were supported
in the generics side of things. I'll have to digest that further to see
what it actually allows. (I'm too tired to do so at the minute - to
properly understand covariance and contravariance I have to be pretty
wide-awake, which isn't compatible with having young twins.)

Nice job on summarising things - and I'm glad my distinction made at
least a *bit* of sense in the end :)

Jon

Jul 3 '06 #12

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Matthew W. Jackson | last post by:
I had a question about the "using" statement and Generics in the next version of C#, and I was directed to this newsgroup. My question is: Will the following syntax be valid? using...
16
by: bigtexan | last post by:
I would like to do the following and cannot figure it out. public class A<T> { public delegate T GetValueDelegate(A<T> var); public GetValueDelegate GetValue = new GetValueDelegate(B.GetValue);...
12
by: Michael S | last post by:
Why do people spend so much time writing complex generic types? for fun? to learn? for use? I think of generics like I do about operator overloading. Great to have as a language-feature, as...
1
by: Peter Kirk | last post by:
Hi I have never used generics before, and I was wondering if the following sort of use was acceptable/normal for a method: public IList<IPerson> GetPersons() { IList<IPerson> personList =...
9
by: sloan | last post by:
I'm not the sharpest knife in the drawer, but not a dummy either. I'm looking for a good book which goes over Generics in great detail. and to have as a reference book on my shelf. Personal...
3
by: Showjumper | last post by:
Back in asp.net 1.1 i made custom collection classes per Karl Seguin's article On the Way to Mastering ASP.NET: Introducing Custom Entity Classes to take advantage of strongly typed data. Now with...
1
by: Kevin S. Goff | last post by:
Hi, all, Hopefully this will make sense: I have 2 classes that implement the same generic interface. public interface IAgingReport<T> { T GetAgingReport(DateTime dAsOfDate); }
14
by: cwineman | last post by:
Hello, I'm hoping to do something using Generics, but I'm not sure it's possible. Let's say I want to have a bunch of business objects and a data access class cooresponding to each business...
4
by: DeveloperX | last post by:
I'm having a play with EventHandlerList but the documentation is a bit ropey and I can't find any decent examples. It also doesn't seem to do what I was led to believe it would. I was under the...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
1
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.