Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / August 2006

Tip: Looking for answers? Try searching our database.

Generics Help

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Bryan Kyle - 12 Aug 2006 21:45 GMT
Hi All,

I'm fairly new to C# and Generics and I'm wondering if anyone has some
suggestions for me.

I'm trying to implement a simple DAO framework using generics to keep
my code as clean as I can, however I'm getting an error with what seems
to me to be correct code.  The error I'm getting is:

Error    1    Cannot implicitly convert type 'Sample.PersonDao' to
'Sample.Dao<Sample.Model>'    F:\Documents and Settings\Bryan\My
Documents\Visual Studio
2005\Projects\EventManager\EventManager\Sample.cs    22    24    EventManager

Below is a snippet of code that will reproduce compilation error I
receive.

namespace Sample
{
   // Base class for all Model objects.
   abstract class Model { }

   // Base class for all Data Access Objects.
   abstract class Dao<TModel> where TModel : Model { }

   // Person model.
   class Person : Model { }

   // Data Access Object for a Person model.
   class PersonDao : Dao<Person> { }

   // Factory that returns the DAO for a model.
   class DaoFactory
   {
       public Dao<Model> GetDao(Model model)
       {
           if (model is Person)
           {
               // Compilation error on the next line.
               return new PersonDao();
           }

           return null;

       }
   }
}

Am I doing something completely wrong here?  Is there something I
should be doing?  Is there an alternate approach that might work
better?

Thanks in advance,
Bryan Kyle
Tom Spink - 12 Aug 2006 23:11 GMT
> Hi All,
>
[quoted text clipped - 50 lines]
> Thanks in advance,
> Bryan Kyle

Hi Bryan,

Unfortunately, this version of C# does not support
contravariance/covariance, which is what you're trying to do.

For example, List<A> is not the same, and is not a subset of List<B> even if
A inherits from B.

Signature

Hope this helps,
Tom Spink

Google first, ask later.

David Browne - 13 Aug 2006 00:05 GMT
>> Hi All,
>>
[quoted text clipped - 60 lines]
> if
> A inherits from B.

Actually this will work, it's just hard to convince the compiler that it
will work.  In the sample below I made two small changes.

First I introduced a non-generic superclass Dao over Dao<TModel>.  Without
this Dao<TModel> is a family of completely unrelated types, each the root of
its own inheritence hierarchy.

Second I declared the method GetDao as a generic method, and introduced an
obfuscating (object) cast to supress the compiler warning and changed the
type comparison from "is" to "==" because "is" includes subclasses, and this
isn't what you want:

     public static Dao<TModel> GetDao<TModel>() where TModel : Model
     {
       if (typeof(TModel) == typeof(Person))
       {
         // Compilation error on the next line.
         return (Dao<TModel>)(object)new PersonDao();
       }

Complete sample:

 namespace Sample
 {
   // Base class for all Model objects.
   abstract class Model { }

   // Base class for all Data Access Objects.
   abstract class Dao {  }

   // Family of base classes.
   abstract class Dao<TModel> : Dao where TModel : Model
   {

   }

   // Person model.
   class Person : Model { }

   // Data Access Object for a Person model.
   class PersonDao : Dao<Person>
   {

   }

   // Factory that returns the DAO for a model.
   class DaoFactory
   {
     public static Dao<TModel> GetDao<TModel>() where TModel : Model
     {
       if (typeof(TModel) == typeof(Person))
       {
         // Compilation error on the next line.
         return (Dao<TModel>)(object)new PersonDao();
       }

       return null;

     }
   }
 }

David
Barry Kelly - 13 Aug 2006 00:36 GMT
> > Unfortunately, this version of C# does not support
> > contravariance/covariance, which is what you're trying to do.
[quoted text clipped - 5 lines]
> Actually this will work, it's just hard to convince the compiler that it
> will work.

There are always workarounds for genuinely type-safe scenarios - but
your modifications don't amount to a contradiction, since your code
doesn't enable covariance :)

-- Barry

Signature

http://barrkel.blogspot.com/

Bryan Kyle - 13 Aug 2006 17:25 GMT
Thanks David!

Your suggestions helped me quite a bit.  I've taken your sample code
and added a driver for it to see how it works and it works great!

Below is the sample working code + driver in case anyone else runs
across a similar issue.

using System;

namespace Sample
{
   // Base class for all Model objects.
   abstract class Model { }

   abstract class Dao { }

   // Base class for all Data Access Objects.
   abstract class Dao<TModel> : Dao where TModel : Model
   {
       public abstract TModel Get();
   }

   // Person model.
   class Person : Model { }

   // Data Access Object for a Person model.
   class PersonDao : Dao<Person>
   {
       public override Person Get()
       {
           return new Person();
       }
   }

   // Thing model.
   class Thing : Model { }

   // Data Access Object for a Thing model.
   class ThingDao : Dao<Thing>
   {
       public override Thing Get()
       {
           return new Thing();
       }
   }

   // Factory that returns the DAO for a model.
   class DaoFactory
   {
       public Dao<TModel> GetDao<TModel>() where TModel : Model
       {
           Dao dao = null;
           if (typeof(TModel) == typeof(Person))
           {
               dao = new PersonDao();
           }
           else if (typeof(TModel) == typeof(Thing))
           {
               dao = new ThingDao();
           }
           return (Dao<TModel>)dao;
       }
   }

   static class Driver
   {
       static void Main()
       {
           DaoFactory factory = new DaoFactory();

           Dao<Person> personDao = factory.GetDao<Person>();
           Console.WriteLine("DAO is " + personDao);
           Person p = personDao.Get();
           Console.WriteLine("Model is " + p);

           Dao<Thing> thingDao = factory.GetDao<Thing>();
           Console.WriteLine("DAO is " + thingDao);
           Thing t = thingDao.Get();
           Console.WriteLine("Model is " + t);

       }

   }
}

> >> Hi All,
> >>
[quoted text clipped - 124 lines]
>
> David
Joanna Carter [TeamB] - 13 Aug 2006 18:41 GMT
| Below is the sample working code + driver in case anyone else runs
| across a similar issue.

Iµ'm sorry, but I really have to ask why on earth you are doing the extra
step of inheriting from a generic class just to get classes that are
differently named ??

|    // Data Access Object for a Person model.
|    class PersonDao : Dao<Person>
[quoted text clipped - 4 lines]
|        }
|    }

This is totally unnecessary. What is wrong with just using Dao<Person > or
Dao<Thing> ?

Then you don't even need your factory :

public class Dao<modelT>
{
 private modelT model;

 public Dao(modelT model)
 {
   this.model = model;
 }

 public modelT Model
 {
   get { return model; }
 }
}

{
 Person p = new Person();

 Dao dao = new Dao<Person>(p);

 Model model = dao.Get();

// or you could assign it straight itno a Person, if you could be sure of
the type...

 if (dao is Dao<Person>)
   Person person = (Person) dao.Model

 ...
}

Or am I missing something ?

Joanna

Signature

Joanna Carter [TeamB]
Consultant Software Engineer

David Browne - 14 Aug 2006 04:29 GMT
> | Below is the sample working code + driver in case anyone else runs
> | across a similar issue.
[quoted text clipped - 14 lines]
> This is totally unnecessary. What is wrong with just using Dao<Person > or
> Dao<Thing> ?

Presumably because PersonDao has additional implementation logic particular
to Person.   Typically this would be somethind like key access or lookup
methods. EG:

  public Person Lookup(string FirstName, string LastName)

This obviously can't go in the definition of Dao<TModel> since it applies
only for a particular TModel type.

David
Bryan Kyle - 14 Aug 2006 19:42 GMT
> | Below is the sample working code + driver in case anyone else runs
> | across a similar issue.
>
> Iµ'm sorry, but I really have to ask why on earth you are doing the extra
> step of inheriting from a generic class just to get classes that are
> differently named ??

The reason I'm wanting to use generics for this is so that I can define
a parameterized interface for the ModelDAO objects.  For example, each
ModelDAO needs to have a Get method that is passed a type of Model and
will return an instance of the same type.

e.g.

public class PersonDAO
{
 public Person Get(Person person);
}

public class ThingDAO
{
 public Thing Get(Thing thing);
}

Had I not used generics for this, then implementing these methods would
be either left up to a development policy, or written generically:

public class PersonDAO
{
 public Model Get(Model model);
}

While there's nothing wrong with that approach, it doesn't seem as
clean and again it is left up to the developer and the runtime
environment to determine if a given Model can be persisted using a
given ModelDAO.

> |    // Data Access Object for a Person model.
> |    class PersonDao : Dao<Person>
[quoted text clipped - 44 lines]
>
> Joanna
Tom Spink - 13 Aug 2006 17:35 GMT
>>> Hi All,
>>>
[quoted text clipped - 124 lines]
>
> David

Hi David,

That's not contravariance.

Signature

Hope this helps,
Tom Spink

Google first, ask later.

David Browne - 14 Aug 2006 04:39 GMT
>>>> Hi All,
>>>>
>>>> I'm fairly new to C# and Generics and I'm wondering if anyone has some
>>>> suggestions for me.

...

>> David
>
> Hi David,
>
> That's not contravariance.

No indeed.  But contravariance/covariance of generic parameters wasn't the
goal, and it wasn't really the problem with the OP's code.

The problem was that the return type of the GetDao method was just wrong.

       public Dao<Model> GetDao(Model model)

Even if it had compiled, it's broken since a Dao<Model> is pretty useless,
and would require a client-side downcast to be at all useful.

       public Dao<TModel> GetDao<TModel>() where TModel : Model

Avoids the problems by returning the type that the client actually wants to
use.

David
Barry Kelly - 13 Aug 2006 00:16 GMT
> I'm trying to implement a simple DAO framework using generics to keep
> my code as clean as I can, however I'm getting an error with what seems
> to me to be correct code.

The thing you are running into is called generic covariance, which C#
doesn't support. Basically, a List<Pigeon> is not a List<Animal>. Why?
Well, if it were true, you'd be able to do this:

 List<Animal> animals = new List<Pigeon>(pigeons);
 animals.Add(new Cat());

... and that would really throw the cat amongst the pigeons, since it
would break type safety.

(The analogue in your example is that Dao<Person> is not a Dao<Model>.)

The solution to your problem is somewhat complex, and I've gone into
detailed explanations of similar situations on this newsgroup in the
past - search for generics covariance, about 2-6 weeks ago, you should
find it.

-- Barry

Signature

http://barrkel.blogspot.com/

Rick - 13 Aug 2006 15:25 GMT
Look for the "Liskov substitution principle"

I also saw a great explanation of this on www.dnrtv.com show number 0009,
where Venkat Subramaniam explained this in detail.

Cheers,
Rick

> Hi All,
>
[quoted text clipped - 50 lines]
> Thanks in advance,
> Bryan Kyle

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.