Pergunta

Eu tenho tentado criar uma classe base simples que encapsula algumas das minhas convenções para o acesso ao banco de dados. Geralmente, crio um SPROC chamado "Products_retieve_product" para selecionar um produto baseado no ProductID. Eu gostaria que o método "recupere" na classe base retorne o tipo que a classe derivada fornece em sua definição. O que estou tentando realizar é possível com os 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;
    }
}
Foi útil?

Solução

Eu acho que você quer fazer algo assim:

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 você executa este programa, o nome de tipo de foo é impresso na linha de comando. Obviamente, este é um exemplo artificial, mas talvez você possa fazer algo mais útil ao carregar de um banco de dados em MyBaseClass.Retrieve().

A chave é adicionar uma restrição a T para que ela deve ser uma instância da própria classe. Dessa forma, você pode especificar a subclasse como tipo genérico ao subclasse MyBaseClass<T>.

Não tenho certeza se isso é uma boa ideia ou não, mas parece que pode ser feito.

Outras dicas

Claro. No seu exemplo, se eu quisesse que minha aula Foo retornasse as barras quando recuperar (...) é chamado:

public class Foo : MyBaseClass<Bar>{}

Hmm, estamos usando a reflexão para obter o nome da classe. Boloader está sem dúvida usando a reflexão para carregar algumas propriedades. Por que não se comprometer totalmente com a reflexão?

Boloader não se importa com este jogo do "Tipo de Retorno". Por que deverí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);
            }
        }
    }
}

Então você apenas diz:

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

Não, não é, porque o que você realmente precisa ser capaz de fazer é algo assim na definição da classe:

public class MyBaseClass<T> : T

Que atualmente não é possível com genéricos.

O que você precisa fazer é separar a fábrica do que a fábrica produz (você precisa de uma classe separada que construirá T e, em seguida, você deve fornecer métodos auxiliares para trabalhar em t, possivelmente métodos de extensão).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top