Pregunta

He estado tratando de crear una clase base simple que encapsula algunas de mis convenciones para el acceso a la base de datos. Por lo general, creo un sproc denominado " products_retrieve_product " para seleccionar un producto basado en productID. Me gustaría que el método " Recuperar " en la clase base para devolver el tipo que la clase derivada proporciona en su definición. ¿Lo que estoy tratando de lograr es posible con los genéricos?

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;
    }
}
¿Fue útil?

Solución

Creo que quieres hacer algo como esto:

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

Cuando ejecuta este programa, el nombre de tipo de Foo se imprime en la línea de comandos. Obviamente, este es un ejemplo artificial, pero quizás pueda hacer algo más útil al cargar desde una base de datos en MyBaseClass.Retrieve () .

La clave es agregar una restricción en T para que sea una instancia de la clase en sí. De esta manera, puede especificar la subclase como el tipo genérico al subclasificar MyBaseClass<T>.

No estoy completamente seguro de si es una buena idea o no, pero parece que se puede hacer.

Otros consejos

Claro. En su ejemplo, si quisiera que mi clase Foo devuelva Barras cuando se llame a Recuperar (...):

public class Foo : MyBaseClass<Bar>{}

Hmm, estamos usando la reflexión para obtener el nombre de clase. Sin duda, BOLoader está utilizando la reflexión para cargar algunas propiedades. ¿Por qué no comprometernos plenamente con la reflexión?

BOLoader no se preocupa por este juego de " tipo de retorno " ;. ¿Por qué deberíamos?

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

Entonces solo dices:

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

No, no lo es, porque lo que realmente necesitas para poder hacer es algo como esto en la definición de la clase:

public class MyBaseClass<T> : T

Lo que actualmente no es posible con los genéricos.

Lo que debe hacer es separar la fábrica de lo que produce la fábrica (necesita una clase separada que compile T y luego debe proporcionar métodos de ayuda para trabajar en T, posiblemente métodos de extensión).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top