Question

I've been trying to create a simple base class that encapsulates some of my conventions for database access. I generally create a sproc named "products_retrieve_product" to select a product based on productID. I would like the method "Retrieve" in the base class to return the type that the derived class supplies in it's definition. Is what I'm trying to accomplish possible with Generics?

public class MyBaseClass <T>
{
    private string _className;

    public MyBaseClass ()
    {
        _className = this.GetType().Name;
    }

    public virtual T Retrieve(int ID)
    {
        Database db = DatabaseFactory.CreateDatabase();
        DbCommand dbCommand = db.GetStoredProcCommand(String.Format("{0}s_Retrieve_{0}", _className));
        db.AddInParameter(dbCommand, String.Format("@{0}ID", _className), DbType.Int32, ID);

        using (IDataReader dr = db.ExecuteReader(dbCommand))
        {
            if (dr.Read())
            {
                BOLoader.LoadDataToProps(this, dr);
            }
        }
        return (T)this;
    }
}
Was it helpful?

Solution

I think that you want to do something like this:

class MyBaseClass<T> where T : MyBaseClass<T>, new()
{
    public T Retrieve()
    {
        return new T();
    }
}

class Foo : MyBaseClass<Foo>
{
}

class Program
{
    public static void Main()
    {
        Foo f = new Foo();
        Foo f2 = f.Retrieve();
        Console.WriteLine(f2.ToString());
    }
}

When you run this program, the type name of Foo is printed on the command line. Obviously this is a contrived example, but maybe you can do something more useful when loading from a database in MyBaseClass.Retrieve().

The key is to add a constraint on T so that it must be an instance of the class itself. This way you can specify the subclass as the generic type when subclassing MyBaseClass<T>.

I'm not entirely sure if this is a good idea or not, but it looks like it can be done.

OTHER TIPS

Sure. In your example, if I wanted my Foo class to return Bars when Retrieve(...) is called:

public class Foo : MyBaseClass<Bar>{}

Hmm, we're using reflection to get the classname. BOLoader is doubtlessly using reflection to load some properties. Why not fully commit to reflection?

BOLoader doesn't care about this game of "return type". Why should we?

public static class MyBaseClassExtender
{
    public static void Retrieve(this MyBaseClass entity, int ID)
    {
        string className = entity.GetType().Name;
        Database db = DatabaseFactory.CreateDatabase();
        DbCommand dbCommand = db.GetStoredProcCommand(String.Format("{0}s_Retrieve_{0}", className));
        db.AddInParameter(dbCommand, String.Format("@{0}ID", className), DbType.Int32, ID);

        using (IDataReader dr = db.ExecuteReader(dbCommand))
        {
            if (dr.Read())
            {
                BOLoader.LoadDataToProps(this, dr);
            }
        }
    }
}

Then you just say:

Foo myFoo = new Foo();
myFoo.Retrieve(2);

No, it is not, because what you really need to be able to do is something like this in the class definition:

public class MyBaseClass<T> : T

Which currently isn't possible with generics.

What you need to do is separate the factory from what the factory produces (you need a separate class which will build T and then you should provide helper methods to work on T, possibly extension methods).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top