Question

J'ai essayé de créer une classe de base simple qui encapsule certaines de mes conventions pour l'accès à la base de données. Je crée généralement un sproc nommé "products_retrieve_product". pour sélectionner un produit basé sur productID. Je souhaite utiliser la méthode "Récupérer". dans la classe de base pour renvoyer le type fourni par la classe dérivée dans sa définition. Est-ce que ce que j'essaie d'accomplir est possible avec 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;
    }
}
Était-ce utile?

La solution

Je pense que vous voulez faire quelque chose comme ceci:

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());
    }
}

Lorsque vous exécutez ce programme, le nom du type de Foo est imprimé sur la ligne de commande. Évidemment, il s'agit d'un exemple artificiel, mais vous pouvez peut-être faire quelque chose de plus utile lors du chargement depuis une base de données dans MyBaseClass.Retrieve () .

La clé est d’ajouter une contrainte sur T afin qu’il s’agisse d’une instance de la classe elle-même. De cette manière, vous pouvez spécifier la sous-classe comme type générique lors de la sous-classe MyBaseClass < T >.

Je ne suis pas tout à fait sûr que ce soit une bonne idée ou non, mais il semble que cela puisse être fait.

Autres conseils

Bien sûr. Dans votre exemple, si je voulais que ma classe Foo renvoie des barres lorsque Retrieve (...) est appelé:

public class Foo : MyBaseClass<Bar>{}

Hmm, nous utilisons la réflexion pour obtenir le nom de la classe. BOLoader utilise sans doute la réflexion pour charger certaines propriétés. Pourquoi ne pas s'engager pleinement dans la réflexion?

BOLoader ne s’intéresse pas à ce jeu de "type de retour". Pourquoi devrions-nous?

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);
            }
        }
    }
}

Ensuite, vous dites simplement:

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

Non, ce n'est pas le cas, car vous devez vraiment pouvoir faire quelque chose comme ceci dans la définition de la classe:

public class MyBaseClass<T> : T

Ce qui actuellement n’est pas possible avec les génériques.

Ce que vous devez faire est de séparer l’usine de ce qu’elle fabrique (vous avez besoin d’une classe séparée qui construira T, puis vous devrez fournir des méthodes auxiliaires pour travailler sur T, éventuellement avec des méthodes d’extension).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top