Я пытаюсь найти хороший способ применить изменение API, которое мы недавно претерпели, которое "ослабляет" тип одного из наших объектов.
Пользователи имели обыкновение предоставлять список ссылок на "Собак". Мы приняли решение, что теперь мы принимаем "Собак" и "Кошек".
Клиентская библиотека использует строго типизированные ссылки, поэтому в настоящее время свойство выглядит следующим образом:
public List<IReference<Dog>> occupants { get; set; }
Я подумал изменить его на:
public List<IReference<Animal>> occupants { get; set; }
Но мне не нравится то, что раньше было проверкой во время компиляции, теперь проверкой во время выполнения (и на стороне сервера). Я бы предпочел иметь что-то более строго набранное, например:
public List<Occupant> occupants { get; set; }
public class Occupant : IReference<Animal> {
IReference<Animal> _internalReference;
public Occupant(IReference<Dog> dog) { _internalReference = dog }
public Occupant(IReference<Cat> cat) { _internalReference = cat }
//... (Implement IReference<Animal> as a pass-through to _internalReference)
}
К сожалению, поскольку C # не позволяет неявным операторам выполнять преобразование из интерфейса, существующие пользователи, обновляющие свою клиентскую библиотеку, должны будут подвергнуться сотням строк изменения кода, чтобы "обернуть" все свои существующие ссылки Dog
в Occupant
конструктор.
Итак, затем я подумал о создании нового класса для списка с некоторыми вспомогательными методами:
public class OccupantList : List<Occupant> {
public void Add(IReference<Dog> newDog) => base.Add(new Occupant(newDog));
public void Add(IReference<Cat> newCat) => base.Add(new Occupant(newCat));
// For backwards compatibility with new list assignments
public static implicit operator OccupantList(List<IReference<Dog>> legacy) =>
new OccupantList(legacy.Select(dog => new Occupant(dog)));
}
К сожалению, C # не предоставляет возможности переопределить базовую реализацию ICollection.Add
за кулисами, которая используется всеми видами утилит отражения и моим сериализатором JSON для заполнения этого списка, поэтому он жалуется, когда ему дают объекты для добавления, которые отсутствуют. уже не типа Occupant
. Я лично столкнулся с проблемами с десериализатором JSON, который я использовал. Я мог бы написать собственный десериализатор для этого класса, но я воспринял это как признак того, что попытка наследовать List<Occupant>
и добавить поддержку типов, которые не наследуются от Occupant
, была плохой идеей, обреченной на неудачу.
У кого-нибудь есть идеи или примеры, которые могут направить нас в правильном направлении?
Это только "моя проблема"
Настоящим кикером является то, что это не является обратной несовместимостью на уровне API. API использует динамическую типизацию (JSON с интерпретатором Python), поэтому единственное изменение заключается в следующем JSON:
{ "occupants": [{"ref_id":"abc123"}, {"ref_id":"zzz999"}] }
становится:
{ "occupants": [{"ref_id":"abc123", "ref_type": "Dog"}, {"ref_id":"gdr736", "ref_type":"Cat"}] }
С правилами обратной совместимости, которые рассматривают ссылку как "Собаку", если тип не указан.
К сожалению, потому что я принял раннее решение добавить типирование во время компиляции к этим ссылочным объектам, пользователи клиентской библиотеки C # - единственные, кто страдает от ослабления типа, тогда как API все еще в состоянии определить подходящий тип динамически во время выполнения.