尝试创建服务以返回具有许多共享属性的对象,但在某些情况下,一个属性应该受到高度限制。这导致奇怪和不期望的行为,其中属性名称在序列化输出中重用,导致浏览器中的行为不正确。这是一个可以粘贴到LINQPad的示例(如果您添加对System.Web.Extensions
的引用)以查看问题:
void Main()
{
System.Web.Script.Serialization.JavaScriptSerializer json = new System.Web.Script.Serialization.JavaScriptSerializer();
json.Serialize(new Full(true)).Dump();
json.Serialize(new Limited()).Dump();
}
public class Full
{
public String Stuff { get { return "Common things"; } }
public FullStatus Status { get; set; }
public Full(bool includestatus)
{
if(includestatus)
Status = new FullStatus();
}
}
public class Limited : Full
{
public new LimitedStatus Status { get; set; }
public Limited() : base(false)
{
Status = new LimitedStatus();
}
}
public class FullStatus
{
public String Text { get { return "Loads and loads and loads of things"; } }
}
public class LimitedStatus
{
public String Text { get { return "A few things"; } }
}
打印:
{"Stuff":"Common things","Status":{"Text":"Loads and loads and loads of things"}}
{"Status":{"Text":"A few things"},"Stuff":"Common things","Status":null}
在浏览器中调用JSON.parse时,第二个状态会覆盖第一个状态,这意味着状态始终为空。
我可以看到修复此问题的唯一方法是重构,以便FullStatus和LimitedStatus都从公共父级继承并使用override
而不是new
- 在现实代码中更复杂一点,但可能。我的假设是否正确?而且,我有兴趣知道这是预期的行为还是错误。
答案 0 :(得分:1)
是的,你的假设是正确的。 new
关键字与override
不同;它只是"隐藏"基类属性,但原始属性仍然存在,仍然可以通过反射发现(这是序列化程序如何工作)。
通常,它被视为" code smell"在基类中定义方法或属性,然后将其替换为派生类中的另一个方法或属性,它取消了基类的功能。这违反了Liskov Substitution Principle。
我建议您为它们创建一个抽象基类,并将常用内容放在那里,而不是从Limited
派生Full
。然后,您可以将事物添加到每个子类中,每个子类对于每个子类都是不同的或排他的(即您的不同类型的Status
成员)。
例如:
class Program
{
static void Main(string[] args)
{
System.Web.Script.Serialization.JavaScriptSerializer json =
new System.Web.Script.Serialization.JavaScriptSerializer();
Console.WriteLine(json.Serialize(new Full(true)));
Console.WriteLine(json.Serialize(new Limited()));
}
}
public abstract class Base
{
public String Stuff { get { return "Common things"; } }
}
public class Full : Base
{
public FullStatus Status { get; set; }
public Full(bool includestatus)
{
if (includestatus)
Status = new FullStatus();
}
}
public class Limited : Base
{
public LimitedStatus Status { get; set; }
public Limited()
{
Status = new LimitedStatus();
}
}
public class FullStatus
{
public String Text { get { return "Loads and loads and loads of things"; } }
}
public class LimitedStatus
{
public String Text { get { return "A few things"; } }
}
输出:
{"Status":{"Text":"Loads and loads and loads of things"},"Stuff":"Common things"}
{"Status":{"Text":"A few things"},"Stuff":"Common things"}