Domanda

Ho provato a creare una semplice classe base che incapsula alcune delle mie convenzioni per l'accesso al database. Generalmente creo uno sproc chiamato "products_retrieve_product" per selezionare un prodotto in base a productID. Vorrei il metodo "Recupera" nella classe base per restituire il tipo fornito dalla classe derivata nella sua definizione. Ciò che sto cercando di realizzare è possibile con 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;
    }
}
È stato utile?

Soluzione

Penso che tu voglia fare qualcosa del genere:

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

Quando si esegue questo programma, il nome del tipo di Foo viene stampato sulla riga di comando. Ovviamente questo è un esempio inventato, ma forse puoi fare qualcosa di più utile quando carichi da un database in MyBaseClass.Retrieve () .

La chiave è aggiungere un vincolo su T in modo che debba essere un'istanza della classe stessa. In questo modo è possibile specificare la sottoclasse come tipo generico durante la sottoclasse MyBaseClass<T>.

Non sono del tutto sicuro che questa sia una buona idea o meno, ma sembra che possa essere fatto.

Altri suggerimenti

Certo. Nel tuo esempio, se volessi che la mia classe Foo restituisse Bars quando Retrieve (...) si chiama:

public class Foo : MyBaseClass<Bar>{}

Hmm, stiamo usando reflection per ottenere il nome della classe. BOLoader utilizza senza dubbio la riflessione per caricare alcune proprietà. Perché non impegnarsi completamente nella riflessione?

A BOLoader non interessa questo gioco di "quot tipo di ritorno". Perché dovremmo?

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

Quindi dici solo:

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

No, non lo è, perché ciò che devi davvero essere in grado di fare è qualcosa di simile nella definizione della classe:

public class MyBaseClass<T> : T

Che attualmente non è possibile con generici.

Quello che devi fare è separare la fabbrica da ciò che la fabbrica produce (hai bisogno di una classe separata che costruirà T e quindi dovresti fornire metodi di supporto per lavorare su T, possibilmente metodi di estensione).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top