1

Hello I'm using Visual Studio 2005 (because I need compact framework support) and my problem is with generics.

I have created an abstract class called AbstractDAO which is my base

From that I am creating other classes like DocumentDAO,HeaderDAO etc which represent different tables on my database

What I wish to do is retrieve a certain number of the above mentioned DAO classes, but as an AbstractDAO (the abstract class has a number of concrete implementations that I wish to use)

What I tried is

AbstractDAO<object> dao = new DocumentDAO();
AbstractDAO<object> dao = (AbstractDAO<object>)new DocumentDAO();
AbstractDAO<T> dao = new DocumentDAO();

I need the above because I have created a function that transfers data from one table to another similar table in a different database, so it would (if it worked) go something like this

AbstractDAO<object> dao_local = new DocumentDAO(local_database);
AbstractDAO<object> dao_remote = new DocumentDAO(remote_database);      
do_transfer(dao_local,dao_remote)

void do_transfer(AbstractDAO<object> from, AbstractDAO<object> to) {
     List<object> items = from.get_all();
     to.insert(items);
}

Is there any way to do the above?

Cruces
  • 3,029
  • 1
  • 26
  • 55
  • Dude - how do You excepct to be able to assign a value of type B to a variable of type A (at least that's what Your first line does..) – Marty Jan 28 '16 at 10:26
  • This is like c# basics : AbstractDOA doa = new DocumentDOA(); or – Marty Jan 28 '16 at 10:27
  • This will only work if you class hierachy is like: – Jens Jan 28 '16 at 10:30
  • because B is an implementation/subclass of A. using AbstractDOA doa = new DocumentDOA() throws an exception saying Using the generic type 'AbstractDAO' requires '1' type arguments btw what you said does indeed work in Java , but it seems that it does not work in c# – Cruces Jan 28 '16 at 10:35
  • 1
    https://stackoverflow.com/questions/8766794/how-to-create-generic-data-access-object-dao-crud-methods-with-linq-to-sql – Arie Jan 28 '16 at 10:37
  • yes my problem is similar (without the LINQ) only imagine if I tried to cast (in that example) CustomerRepository to a variable of type Repository – Cruces Jan 28 '16 at 10:42
  • Did you try [Automapper](http://automapper.org/)? This framework can map similar objects (objects with similar field names) without configuration. – Kovalenko Ivan Jan 28 '16 at 10:42

4 Answers4

1

That will only work if your class hierachy is like this:

class DocumentDAO : AbstractDAO<object> {
    //...
}

By your comment it seems like you have a type hierarchy like this:

class DocumentDAO : AbstractDAO<SomeType> {
    public override SomeType Foo() {
        //...
        return someValue;
    }
    //...
}
class AbstractDAO<T> {
    public abstract T Foo();
    //...
}

You probably want to refactor AbstractDAO to implement a non generic interface like IAbstractDAO:

class IAbstractDAO {
    object Foo();
    //...
}

class AbstractDAO<T> {
    public object Foo() {
        return Foo();
    }
    public abstract T Foo();
    //...
}
Jens
  • 2,327
  • 25
  • 34
  • My hierarchy is like the one you posted last (without the interface) `class AbstractDAO { } class DocumentDAO : AbstractDAO {}` But adding this: ` public List get_objects() { List items = get_all(); return items as List; } public int insert_object(object obj) { return insert((T)obj); }` in abstract dao seems to not throw errors so I guess I can use that, it's not exactly what I wanted (i.e. being able to cast the subclass to the superclass) but I can work with that , thanks – Cruces Jan 28 '16 at 10:56
1

Any implementation of AbstractDAO<T> is compiled to a separate object type where T is replaced with the type. See "Is generics runtime or compile time polymorphism?" for more information on how this happens. In short, don't let the <T> fool you.

This means that you can't assign DocumentDAO to AbstractDAO<object> any more than you can assign String to it. Also a generic type is not the same as inheriting, which seems to be what you are trying to achieve.

In either case there are two standard solutions, as already mentioned.

The first is to operate on interfaces. You create an interface for the common properties and have AbstractDAO<T> or any other inherit from this. Then most of the time you simply operate on interfaces. Exactly how you organize it is up to you.

The second is to perform a shallow copy of the object. This means copying values and references from one object to the other. For this you usually use an object mapper like AutoMapper. This tutorial should get you started..

Community
  • 1
  • 1
Tedd Hansen
  • 12,074
  • 14
  • 61
  • 97
  • Thanks that helped a lot, I went with another solution, I will post it below, but the literature certainly helped me understand the workings of generics better – Cruces Jan 28 '16 at 11:23
0

You can try to use Automapper to transfer your objects like this:

void do_transfer(AbstractDAO<FirstType> from, AbstractDAO<SecondType> to) 
{
    Mapper.Initialize(cfg=>cfg.CreateMap<FirstType, SecondType>);

    List<FirstType> fromItems = from.get_all();
    List<SecondType> itemsToInsert = 
        Mapper.Map<List<FirstType>, List<SecondType>>(fromItems);
    to.insert(itemsToInsert);
}

By default automapper will map fields with same names. You can create configurations for complex type mapping.

  • my problem isn't that I can't convert the type of from to to, my problem is that i want to have a generic as an argument to that function if possible I.e. either `do_transfer(AbstractDAO from, AbstractDAO to)` or `do_transfer(AbstractDAO from, AbstractDAO to)` the thing is that I can't seem to cast AbstractDAO to AbstractDAO even though in theory object is the superclass of all objects – Cruces Jan 28 '16 at 11:01
  • Did you try to declare transfer like this `DoTransfer(Dao source, Dao dest)` ? – Kovalenko Ivan Jan 28 '16 at 11:16
0

So I finally found the answer to what I was trying to do, instead of assigning abstractDAO to something I created a factory that would retrieve the required AbstractDAO according to what type the generic was, and used this function

private bool transfer<T>(){
    AbstractDAO<T> local = DAOFactory.get<T>(local_database);
    AbstractDAO<T> remote = DAOFactory.get<T>(remote_database);
    List<T> items = local.get_all();
    foreach (T item in items) {
        remote.insert(item);
    } 
}

That way I can call this function like this: transfer< Document >(); transfer< Header >();

etc. and do a full transfer

edit: just for completeness' shake this is the factory I created

    public static AbstractDAO<T> get<T>(Database database) {
        Type t = typeof(T);
        if (t == typeof(Document)) {
            return new DocumentDAO(database) as AbstractDAO<T>;
        } else if (t == typeof(Header)) {
            return new HeaderDAO(database) as AbstractDAO<T>;
        } etc.
    }
Cruces
  • 3,029
  • 1
  • 26
  • 55