C# 可包含两种类型项的列表的模式

C# 可包含两种类型项的列表的模式,c#,generics,design-patterns,covariance,backwards-compatibility,C#,Generics,Design Patterns,Covariance,Backwards Compatibility,我正试图找到一种很好的方法来采用我们最近经历的API更改,它“削弱”了我们的一个对象上的类型 用户过去可以提供一个“狗”的参考列表。我们已经决定接受“狗”和“猫” 客户端库使用强类型引用,因此该属性当前看起来像: public List<IReference<Dog>> occupants { get; set; } 不幸的是,C#暴露出无法覆盖核心ICollection.Add实现的幕后操作,所有类型的反射实用程序和我的JSON序列化程序都使用它来填充此列表,因此当给

我正试图找到一种很好的方法来采用我们最近经历的API更改,它“削弱”了我们的一个对象上的类型

用户过去可以提供一个“狗”的参考列表。我们已经决定接受“狗”和“猫”

客户端库使用强类型引用,因此该属性当前看起来像:

public List<IReference<Dog>> occupants { get; set; }
不幸的是,C#暴露出无法覆盖核心
ICollection.Add
实现的幕后操作,所有类型的反射实用程序和我的JSON序列化程序都使用它来填充此列表,因此当给它添加的对象不是
类型时,它会抱怨。我个人在使用JSON反序列化程序时遇到了问题。我可以为这个类编写一个自定义反序列化程序,但我认为尝试继承
List
并添加对不从
occulator
继承的类型的支持是一个注定失败的坏主意

有没有人有任何想法或例子可以引导我们朝着正确的方向前进


这只是“我的问题”

真正重要的是,这甚至不是API层的向后不兼容。API使用动态类型(带有Python解释器的JSON),因此唯一的变化是以下JSON:

{ "occupants": [{"ref_id":"abc123"}, {"ref_id":"zzz999"}] }
变成:

{ "occupants": [{"ref_id":"abc123", "ref_type": "Dog"}, {"ref_id":"gdr736", "ref_type":"Cat"}] }
使用向后兼容规则,如果未指定类型,则将引用视为“Dog”

不幸的是,由于我早期决定将编译时类型添加到这些引用对象中,因此C#client library用户是唯一遭受类型弱化的用户,而API仍然能够在运行时动态地找出合适的类型

最坏情况下: 你会讨厌这个,但是如果这个API有任何消费者,那么最佳实践表明,你的突破性更改意味着现在是时候对你的API进行版本化了

充其量: 我没有看到您提到的API控制器的签名,但是如果所讨论的模型被定义为该方法的一个参数,您可能可以通过重载控制器方法,让模型绑定器处理不同的形状,然后在某种中间件或automapper中解决问题

为什么不通过改变模型来解决呢?
因为听起来你已经为某种外部消费发布了这个API,所以你真的不应该违背输入模型所描述的契约。

这是一个有趣的观点。API本身并不是向后不兼容的,因为它是动态类型的(使用JSON)。我已经更新了我的请求,并提供了一些详细信息。基本上,这是“我的问题”,进行直接JSON通信的API用户不会注意到更改。另一个(突破性的)更改是将
set
访问器更改为
private
(针对
用户
),然后提供
Add(Dog-Dog)
Add(Cat)
方法(而不仅仅是
占用者。添加
)。我还没有真正想清楚这一点,只是一个头脑风暴的想法。。。
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)));
}
{ "occupants": [{"ref_id":"abc123"}, {"ref_id":"zzz999"}] }
{ "occupants": [{"ref_id":"abc123", "ref_type": "Dog"}, {"ref_id":"gdr736", "ref_type":"Cat"}] }