我有一个包含Sync对象的类
[DataContract]
class C1
{
[DataMember]
private object mySyncObject = new object();
}
此对象以前是DataContract的一部分,它是由于没有明显原因而序列化的。由于它不包含任何数据,因此我在同步对象上省略了DataMember。但是由于DataContractSerializer在反序列化期间不会调用默认的ctor,因此当我使用它时会在运行时导致NullReferenceExceptions。
lock(mySyncObject) // NullReferenceException
现在我想在属性中封装锁对象访问,并确保多线程访问不会导致该属性返回null或不同的对象。
我修改后的对象现在看起来像
[DataContract]
class C1
{
private volatile object mySyncObject = new object();
object SyncObject
{
get
{
if (mySyncObject == null)
{
Interlocked.CompareExchange<object>(ref mySyncObject, new object(), null);
}
return mySyncObject;
}
}
}
可以这么容易,还是我错过了潜在的竞争条件?
答案 0 :(得分:1)
由于Interlock.CompareExchange<T>(ref T location1, T value, T comparand)
是atomic operation,您还没有错过潜在的竞争条件。您宁愿创建与Lazy<T>
实现类似的实现。
可以在这个blog上找到一个很好的例子来讨论你正在展示的非常类似的实现(因此,我也不会再次添加实现)。
原始答案,替代OP建议实施
作为替代方案,您也可以在反序列化后使用OnDeserializedAttribute
初始化mySyncObject
,如下例所示
[DataContract]
class C1
{
private object mySyncObject = new object();
[OnDeserialized]
internal void OnDeserialized(StreamingContext context)
{
mySyncObject = new object();
}
}
通过这种方式,您可以在任何地方保留原始逻辑,并且在反序列化发生后直接调用该方法,并初始化您的mySyncObject。
正如我们在评论中所讨论的那样,这与DataContractSerializer
,BinaryFormatter
,JSON.Net
兼容。
有关DataContractSerializer
未使用默认构造函数的原因的详细信息,请参阅此answer