Как вернуть универсальный тип с помощью метода базового класса?
-
03-07-2019 - |
Вопрос
Я пытался создать простой базовый класс, который инкапсулировал бы некоторые из моих соглашений о доступе к базе данных.Обычно я создаю процедуру с именем «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, возможно, методы расширения).