Возможно, я иду об этом неправильно.
У меня есть набор классов, которые происходят из "Образцового" класса, базового класса с набором общей собственности и методов. Я хочу, чтобы они все реализовали ряд функциональности:
public abstract void Create();
public abstract T Read<T>(Guid ID); //<--Focus on this one
public abstract void Update();
public abstract void Delete();
Тогда я реализую его в дочернем классе как "Назначение" как так:
public override T Read<T>(Guid ID)
{
var appt = db.Appointments.First(a => a.AppointmentID.Equals(ID));
var appointment = new Appointment()
{
DateEnd = appt.dateEnd.GetValueOrDefault(),
Location = appt.location,
Summary = appt.summary
};
return appointment;
}
Это выдает исключение, "Не может неявно преобразовать тип 'Назначение' в T". Если я изменяюсь, подпись метода "общественности переопределяют Чтение Назначения (идентификатор Гуида)", тогда в компиляторе говорится, что я не реализовал абстрактный метод в дочернем классе.
Что я пропускаю? Кто-либо может дать мне некоторые примеры кода?
Похоже, вы могли бы использовать общий базовый класс! Рассмотрим что-то вроде следующего:
class Model<T>
{
public abstract T Read(Guid ID);
}
class Appointment : Model<Appointment>
{
public override Appointment Read(Guid ID) { }
}
Теперь все ваши подклассы строго типизированы. Конечно, компромисс заключается в том, что у вас больше нет единого базового класса. Модель <Назначение>
- это не то же самое, что Модель <Заказчик>
. Я обычно не считаю это проблемой, потому что у них мало общих функций - интерфейсы похожи, но все они работают с разными типами.
Если вам нужна общая база, вы, конечно, можете обмануть и реализовать интерфейс на основе объекта
, который выполняет те же общие задачи. Например, что-то в духе (непроверено, но идея есть):
interface IModelEntity
{
object Read(Guid ID);
}
class Model<T> : IModelEntity
{
public T Read(Guid ID)
{
return this.OnRead(ID); // Call the abstract read implementation
}
object IModelEntity.Read(Guid ID)
{
return this.OnRead(ID); // Call the abstract read implementation
}
protected abstract virtual T OnRead(Guid ID);
}
class Appointment : Model<Appointment>
{
protected override Appointment OnRead(Guid ID) { /* Do Read Stuff */ }
}
Будет ли это работать?
public abstract T Read<T>(Guid ID) where T : IAppointment;
Вам нужно боксировать и использовать. Интересно, почему этот метод общий?
return (T)(object)appointment;
Вы должны сначала привести к объекту
, а затем к T
. Причина кроется в том, что объект
находится на вершине цепочки наследования. Нет прямой корреляции между Назначение
и T
; поэтому вам нужно вернуться к объекту
, а затем вернуться к T
.
Я предоставил этот ответ, чтобы объяснить, почему оператор return не будет работать, если он не будет дважды приведен - и в поддержку ответов Хаоса и Грега
Сначала я предлагаю вам превратить ваш базовый класс в интерфейс. Если это вариант для вас, это также сократит немного меньше загроможденного кода, так как вы можете избавиться от ключевых слов abstract
и public
в объявлении интерфейса и опустить переопределить
в реализующих классах.
Во-вторых, , как предлагает ваша реализация Appointment.Read
, вы можете изменить сигнатуру метода Read
, чтобы вернуть объект модели.
Оба предложенных изменения приведут к следующему:
public interface IModel
{
void Create();
IModel Read(Guid ID);
void Update();
void Delete();
}
В-третьих , мне кажется, что Read
действительно должен быть заводским методом. В вашем текущем коде вам нужно сначала создать экземпляр объекта Appointment
, прежде чем вы сможете вызвать метод Read
для получения другого объекта Appointment
. Мне это кажется неправильным с точки зрения дизайна класса.
Как насчет того, чтобы взять Read
из базового класса / интерфейса и предоставить его как статический метод во всех производных / реализующих классах? Например:
public class Appointment : IModel
{
public static Appointment Read(Guid ID)
{
return new Appointment()
{
...
};
}
}
Вы также можете рассмотреть возможность перемещения Read
в статический (фабричный) класс; однако тогда он должен быть достаточно умен, чтобы знать, какой объект он должен вернуть. Это будет работать, например, если бы у вас была таблица в вашей БД, которая сопоставляла GUID с соответствующим типом объекта.
Правка : последнее предложение, приведенное выше, было следующим:
В-третьих, если это до сих пор верно, следующий вопрос будет заключаться в том, следует ли использовать Read
вместо этого статическим методом. .Если это так, его можно сделать статическим
и переместить в статический класс Модель
. Затем метод будет действовать как фабричный метод, который строит объекты IModel
из БД:
Guid guid = ...;
IModel someModel = Model.Read(guid);
в вашем классе Appointment добавьте это
public class Appointment<T> : Model where T : Appointment
Если public abstract T Read
из Model
будет возвращать только производные типы из Model
, подумайте об изменении сигнатуры на
public abstract class Model
{
public abstract void Create();
public abstract Model Read(Guid ID); //<--here
public abstract void Update();
public abstract void Delete();
}
В этой конструкции есть что-то странное.
Независимо от того, является ли класс Model
шаблонным, накладывать параметр шаблона на метод Read
не имеет большого смысла как метод экземпляра.
Обычно у вас должно быть что-то вроде того, что разместил Greg D.