Не удается использовать type в качестве параметра типа из-за отсутствия неявного преобразования ссылок

#c# #generics

#c# #общие

Вопрос:

Я пытаюсь построить следующую структуру. Похоже, компилятор не согласен с тем, что ServiceTicketModel может быть неявно преобразовано в Model<IEntity> . Почему это так и есть ли какой-либо способ обойти это?

 public abstract class ModelTest<TModel> where TModel : Model<IEntity>, new()

public abstract class Model<TEntity> where TEntity : IEntity

public class ServiceTicketModel : Model<ServiceTicket>

public class ServiceTicket : Ticket, IEntity


public class ServiceTicketModelTest : ModelTest<ServiceTicketModel>, IDisposable
  

Ошибка в последнем классе, и сообщение:

The type '...ServiceTicketModel' cannot be used as type parameter 'TModel' in the generic type or method 'ModelTest<TModel>'. There is no implicit reference conversion from '...ServiceTicketModel' to '...Model<IEntity>'

Ответ №1:

компилятор верен… вам нужно создать подкласс, Model<IEntity> чтобы удовлетворить where ограничению, которое вы добавили TModel , но вы этого не делаете — вы создаете подкласс Model<ServiceTicket> where ServiceTicket : IEntity . Это не то же самое.

Если бы это было определение интерфейса или делегата, вы иногда можете использовать модификаторы отклонения ( in / out ), чтобы сделать его более удобным. Но вы не можете сделать это с помощью классов.

Возможно, вам потребуется использовать:

 public abstract class ModelTest<TModel, TEntity>
    where TModel : Model<TEntity>, new()
    where TEntity : IEntity
{...}
public class ServiceTicketModelTest
    : ModelTest<ServiceTicketModel, ServiceTicket>
    , IDisposable
{...}
  

Комментарии:

1. Спасибо, это очень полезно, и я протестирую ваш пример кода. Но можете ли вы объяснить, почему это не одно и то же, или указать мне на документацию, которая объясняет это? Существует неявное преобразование между ServiceTicket и IEntity , так почему бы не между Model<ServiceTicket> и Model<IEntity> ?

2. @MartBroekkamp это проще всего объяснить на примере, подобном List<T> ; представьте, что у вас есть List<ServiceTicket> . Это означает, что вы ожидаете только ServiceTicket , верно? Теперь представьте, что вы можете неявно обрабатывать это как List<IEntity> из-за неявного преобразования. Это означает, что вы можете иметь class Foo : IEntity {...} и добавлять Foo с помощью неявного преобразования. Значение: разрешение List<ServiceTicket> обращаться подобным образом List<IEntity> позволило бы вам нарушить инвариант, который он только принимает ServiceTicket .