反序列化后初始化私有只读字段

13

我需要在反序列化后初始化私有只读字段。我有以下DataContract:

[DataContract]
public class Item
{
    public Item()
    {
        // Constructor not called at Deserialization 
        // because of FormatterServices.GetUninitializedObject is used
        // so field will not be initialized by constructor at Deserialization
        _privateReadonlyField = new object();
    }

    // Initialization will not be called at Deserialization (same reason as for constructor)
    private readonly object _privateReadonlyField = new object();

    [DataMember]
    public string SomeSerializableProperty { get; set; }

    [OnDeserializing]
    public void OnDeserializing(StreamingContext context)
    {
        // With this line code even not compiles, since readonly fields can be initialized only in constructor
        _privateReadonlyField = new object();
    }
}

我需要的是,在反序列化之后 _privateReadonlyField 不为null。

你有没有任何建议或方法可以实现这一点?或者说我需要移除 "readonly" 关键字,但这不是一个好的选择。


你使用了哪种序列化方法?不同的方法需要不同的对象构建方式。 - Joachim Isaksson
_privateReadonlyField 标记为 [DataMember] 有什么问题吗?数据契约序列化程序会毫无问题地处理它。 - Sergey Kalinichenko
Joachim Isaksson:我正在使用DataContractJsonSerializer,但实际上这并不重要——所有序列化器在反序列化时都使用FormatterServices.GetUninitializedObject。 - Andris
1
dasblinkenlight: 我不需要序列化字段的值。我只需要在反序列化我的项时知道这个字段不是空的。 - Andris
2个回答

10

序列化可以读取只读字段的值,因为它使用反射机制,它会忽略访问权限规则。可以说,下面的操作在序列化过程中是合理的,尽管我强烈建议在几乎任何其他情况下都不要这样做:

private readonly Doodad _oldField;

[OptionalField(VersionAdded = 2)]
private readonly Widget _newField;

[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
    if (_oldField != null && _newField == null)
    {
        var field = GetType().GetField("_newField",
            System.Reflection.BindingFlags.Instance |
            System.Reflection.BindingFlags.DeclaredOnly |
            System.Reflection.BindingFlags.NonPublic);
        field.SetValue(this, new Widget(_oldField));
    }
}

1
谢谢回复。当被问到这个问题时,我真的忘记了反射。 - Andris
[OptionalField] 属性对此解决方案有什么影响?如果没有它,OnDeserialized 仍然有效吗? - Tim Long
如果我没记错的话,忽略它会导致反序列化读取不包含该字段的输入时抛出异常。 - Tim Sylvester
这对于在反序列化的引用对象上设置只读字段效果很好,但对于在反序列化的值对象上设置只读字段则完全不起作用,而我正是需要它。仍然是一个很好的答案。 - William
好吧,结果证明这确实有效,但在调用“SetValue”之前必须将“this”装箱,然后通过将其分配给“this”来取消装箱。我不知道您可以为结构体赋值给this。我知道您不能为类这样做。今晚我学到了很多东西。这里是一个展示装箱/取消装箱的Gist:https://gist.github.com/rummelsworth/938180ba2ef4d74d9b8d86dcba295bd6 - William

7
任何声明为 private readonly 的字段可以在声明它的同一行或构造函数内进行实例化。一旦这样做了,就无法更改它。
来自 MSDN

readonly 关键字是您可以用于字段上的修饰符。当字段声明包括 readonly 修饰符时,通过该声明引入的字段的赋值只能作为声明的一部分或在同一类中的构造函数中发生。

这意味着你必须删除 readonly 关键字才能使其正常工作。

谢谢,Huske。不幸的是,你证实了我的感觉,我希望有某种方法可以解决这个问题。 - Andris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接