Дженерики C# - Как я возвращаю определенный тип?

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

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

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". Если я изменяюсь, подпись метода "общественности переопределяют Чтение Назначения (идентификатор Гуида)", тогда в компиляторе говорится, что я не реализовал абстрактный метод в дочернем классе.

Что я пропускаю? Кто-либо может дать мне некоторые примеры кода?

12
задан Rap 11 February 2010 в 18:15
поделиться

9 ответов

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

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 */ }
}
19
ответ дан 2 December 2019 в 04:08
поделиться

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

public abstract T Read<T>(Guid ID) where T : IAppointment;
5
ответ дан 2 December 2019 в 04:08
поделиться

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

return (T)(object)appointment;
4
ответ дан 2 December 2019 в 04:08
поделиться

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

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

3
ответ дан 2 December 2019 в 04:08
поделиться

Сначала я предлагаю вам превратить ваш базовый класс в интерфейс. Если это вариант для вас, это также сократит немного меньше загроможденного кода, так как вы можете избавиться от ключевых слов 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);
2
ответ дан 2 December 2019 в 04:08
поделиться

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

0
ответ дан 2 December 2019 в 04:08
поделиться

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

public class Appointment<T> : Model where T : Appointment
1
ответ дан 2 December 2019 в 04:08
поделиться

Если public abstract T Read(Guid ID); из 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();
}
1
ответ дан 2 December 2019 в 04:08
поделиться

В этой конструкции есть что-то странное.

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

Обычно у вас должно быть что-то вроде того, что разместил Greg D.

1
ответ дан 2 December 2019 в 04:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: