Подтвердить что ты не робот

С# Generics - Как мне вернуть определенный тип?

Возможно, я все это сделаю неправильно.

У меня есть куча классов, которые происходят из класса "Модель", базового класса с кучей общих свойств и методов. Я хочу, чтобы все они реализовали набор функций:

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". Если я изменю подпись метода на "public override Appointment Read (Guid ID)", тогда компилятор говорит, что я не реализовал абстрактный метод в дочернем классе.

Что мне не хватает? Может ли кто-нибудь дать мне несколько примеров кода?

4b9b3361

Ответ 1

Похоже, вы могли бы использовать общий базовый класс! Рассмотрим следующее:

class Model<T>
{
    public abstract T Read(Guid ID);
}

class Appointment : Model<Appointment>
{
    public override Appointment Read(Guid ID) { }
}

Теперь все подклассы строго типизированы. Конечно, компромисс заключается в том, что у вас больше нет единого базового класса. A Model<Appointment> - это не то же самое, что и Model<Customer>. Я вообще не считаю, что это проблема, потому что там мало общих функциональных возможностей - интерфейсы схожи, но все они работают с разными типами.

Если вам нужна общая база, вы можете, конечно, обмануть и реализовать интерфейс на основе object, который выполняет те же общие задачи. Например, что-то в духе (непроверено, но идея там):

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 */ }
}

Ответ 2

Будет ли это работать?

public abstract T Read<T>(Guid ID) where T : IAppointment;

Ответ 3

Вам нужно вставить и бросить. Интересно, но почему этот метод является общим?

return (T)(object)appointment;

Ответ 4

Вы должны набрать object сначала, а затем T. Причина кроется в том, что object находится в верхней части цепочки наследования. Прямой корреляции от Appointment до T нет; поэтому вам нужно отступить до object, а затем вернуться к T.

Я предоставил этот ответ, чтобы дать объяснение, почему оператор возврата не будет работать, если он не будет дважды применен - ​​и в поддержку ответов, данных Хаосом и Грегом

Ответ 5

Сначала, я бы предложил вам превратить ваш базовый класс в интерфейс. Если это вариант для вас, это также уменьшится в чуть менее загроможденном коде, поскольку вы можете избавиться от ключевых слов abstract и public в объявлении интерфейса и опустить override в реализующих классах.

Второй, как предлагает ваша реализация Appointment.Read, вы можете изменить сигнатуру метода Read, чтобы вернуть объект модели.

Оба предложенных изменения приведут к следующему:

public interface IModel
{
    void Create();
    IModel Read(Guid ID);
    void Update();
    void Delete();
}

Третий, мне кажется, что Read действительно должен быть factory. В вашем текущем коде вам нужно сначала создать экземпляр объекта Appointment, прежде чем вы сможете вызвать метод Read для извлечения другого объекта Appointment. Это кажется неправильным для меня, с точки зрения дизайна класса.

Как взять Read из базового класса/интерфейса и предоставить его как статический метод во всех производных/реализующих классах? Например:

public class Appointment : IModel
{
    public static Appointment Read(Guid ID)
    {
        return new Appointment()
               {
                   ...
               };
    }
}

Вы также можете рассмотреть возможность перемещения Read в статический класс (factory); однако тогда он должен быть достаточно умным, чтобы знать, какой объект он должен вернуть. Это будет работать, например. если бы у вас была таблица в вашей БД, которая сопоставила бы идентификатор GUID с соответствующим типом объекта.

Изменить. Последнее предложение выше:

В-третьих, если это так до сих пор, следующий вопрос будет заключаться в том, должен ли вместо Read быть статический метод. Если это так, его можно было бы сделать static и переместить в статический класс Model. Затем метод будет действовать как метод factory, который создает IModel объекты из БД:

Guid guid = ...;
IModel someModel = Model.Read(guid);

Ответ 6

В этом дизайне есть что-то напуганное.

Независимо от того, является ли шаблон Model шаблоном, размещение параметра шаблона в методе Read не имеет большого смысла в качестве метода экземпляра.

Обычно у вас будет что-то вроде того, что размещал Грег Д.

Ответ 7

Если public abstract T Read<T>(Guid ID); of 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();
}

Ответ 8

в вашем классе Appointment добавьте это

public class Appointment<T> : Model where T : Appointment

Ответ 9

вы также можете позвонить: var x = myModel.Read<Appointment>();