Как вернуть универсальный тип с помощью метода базового класса?

StackOverflow https://stackoverflow.com/questions/603596

Вопрос

Я пытался создать простой базовый класс, который инкапсулировал бы некоторые из моих соглашений о доступе к базе данных.Обычно я создаю процедуру с именем «products_retrieve_product», чтобы выбрать продукт на основе идентификатора продукта.Я хотел бы, чтобы метод «Получить» в базовом классе возвращал тип, который предоставляет производный класс в своем определении.Возможно ли то, чего я пытаюсь достичь, с помощью дженериков?

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;
    }
}
Это было полезно?

Решение

Я думаю, что вы хотите сделать что-то вроде этого:

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

Когда вы запускаете эту программу, имя типа Foo печатается в командной строке.Очевидно, что это надуманный пример, но, возможно, вы сможете сделать что-то более полезное при загрузке из базы данных в MyBaseClass.Retrieve().

Ключевым моментом является добавление ограничения на T, чтобы он был экземпляром самого класса.Таким образом, вы можете указать подкласс как общий тип при создании подкласса. MyBaseClass<T>.

Я не совсем уверен, хорошая это идея или нет, но похоже, что это можно сделать.

Другие советы

Конечно.В вашем примере, если бы я хотел, чтобы мой класс Foo возвращал Bars при вызове Retrieve(...):

public class Foo : MyBaseClass<Bar>{}

Хм, мы используем отражение, чтобы получить имя класса.BLoader, несомненно, использует отражение для загрузки некоторых свойств.Почему бы не полностью посвятить себя размышлениям?

BLoader не заботится об этой игре с «типом возврата».Почему мы должны это делать?

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

Тогда вы просто говорите:

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

Нет, это не так, потому что вам действительно нужно уметь что-то вроде этого в определении класса:

public class MyBaseClass<T> : T

Что в настоящее время невозможно с дженериками.

Что вам нужно сделать, так это отделить фабрику от того, что она производит (вам нужен отдельный класс, который будет собирать T, а затем вы должны предоставить вспомогательные методы для работы с T, возможно, методы расширения).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top