2009-08-18 18 views
29

私は初期値が設定された後は不変であるいくつかのクラスを持っています。 Eric Lippertはこれをwrite-once immutabilityと呼んでいます。不変性とXMLシリアル化

C#でのライトワンス不変性の実装は、通常、コンストラクタを使用して初期値を設定することを意味します。これらの値は読み取り専用フィールドを初期化します。

XmlSerializerまたはDataContractSerializerのいずれかを使用して、このようなクラスをXMLにシリアル化する必要がある場合は、パラメータのないコンストラクタが必要です。

誰もこの問題を回避する方法についての提案はありますか?シリアライゼーションでうまくいく他の形式の不変性はありますか?

EDIT:@Toddが指摘したように、DataContractSerializerにはパラメータのないコンストラクタは必要ありません。 DataContractSerializer documentation on MSDNによると、DataContractSerializerは "ターゲットオブジェクトのコンストラクタを呼び出しません。"

public class Immutable 
{ 
    public Immutable(string foo, int bar) 
    { 
     this.Foo = foo; 
     this.Bar = bar; 
    } 

    public string Foo { get; private set; } 
    public int Bar { get; private set; } 
} 

あなたはシリアライズ/デシリアライゼーションの中にその不変オブジェクトを表すためにダミーのクラスを作成することができます:

+1

私はあなたが混乱していると思っています...あなたが言うのは、ポピーシム不変性です。それは、一度だけ書き込み不能な私のように見えます。 –

+0

編集:... is ** ** not popsicle ... –

+0

あなたは絶対に正しいです、@Martinho。私は "追記不可能"を読むように私の質問を修正しました。 – dthrasher

答えて

9

これはあなたの「不変」オブジェクトであると仮定すると、

public class DummyImmutable 
{ 
    public DummyImmutable(Immutable i) 
    { 
     this.Foo = i.Foo; 
     this.Bar = i.Bar; 
    } 

    public string Foo { get; set; } 
    public int Bar { get; set; } 

    public Immutable GetImmutable() 
    { 
     return new Immutable(this.Foo, this.Bar); 
    } 
} 

あなたは型のプロパティを持っている場合変更不可能、シリアル化しないで、代わりにDummyをシリアル化してください。変更可能:

[XmlIgnore] 
public Immutable SomeProperty { get; set; } 

[XmlElement("SomeProperty")] 
public DummyImmutable SomePropertyXml 
{ 
    get { return new DummyImmutable(this.SomeProperty); } 
    set { this.SomeProperty = value != null ? value.GetImmutable() : null; } 
} 

OK、これは少しシンプルに見えるものです...しかし、それはうまくいくはずです;)

+2

これは厄介ですが、Microsoftが推奨するアプローチのようです。 –

8

"Realio-trulio"不変性にはコンストラクタが関係します。あなたは、たとえば、行うことができる場所ポプシクルの不変性は次のとおりです。

Person p = new Person(); 
p.Name = "Fred"; 
p.DateOfBirth = DateTime.Today; 
p.Freeze(); // **now** immutable (edit attempts throw an exception) 

(またはオブジェクト初期化子と同じ)

これは、限り、あなたが何をしに、シリアル化されたコールバックに扱うように、非常によくDataContractSerializerに適合しますFreezeXmlSerializerはシリアル化コールバックを実行しないので、より多くの作業が行われます。

ただし、カスタムシリアル化(IXmlSerializable)を使用するといずれの場合も適しています。同様に、カスタムシリアル化はで、はrealio-trulioの不変性を持つことができますが、苦痛です。 "一度作成してからインターフェイスメソッドを呼び出す"のように嘘です。は不変です。

真の不変性のためには、DTOを使用してください。

+0

「Realio-trulio」の不変性とは何ですか? –

+1

@ChibuezeOpata *実際に*不変(少なくとも不正行為なし) - 'readonly'など –

+0

ハハ、クール、ありがとう! :) –

0

私はリンク先の記事を見ました。彼の用語では、コンストラクタで初期化された読み取り専用フィールドを使用するオブジェクトを「書き込み専用不変性」と呼びます。

「ポプシクル不変性」は少し異なります。 Lippertは、デシリアライゼーション(あなたが解決しようとしている問題)と、AとBを独立して作成する必要があるが、AニーズにはBへの参照があり、BはAへの参照。

この結果を達成するための2つのより明白な方法がここで言及されています(私が実際にこれを書いていたように)。 Thomas Levesqueのパターンは基本的に "Builder"パターンです。しかし、このケースでは、シリアライゼーション/デシリアライゼーションが対称的になるように、BuilderからImmutableだけでなく、ImmutableからBuilderに移行する必要があるため、この場合はむしろ扱いにくいものになります。

Marc Gravellが述べたように、「ロック」パターンが役立つようです。私はC#に精通していないので、実装するにはどうすればよいか分かりません。私は恐らくのような個人的な財産がのようにロックされていると思います。その後、すべてのゲッターメソッドは、オブジェクトがロックされているかどうかを明示的にチェックする必要があります(別名「フリーズ」)。もしそうなら、例外を投げるべきです(ランタイムエラーです;コンパイル時にポップスティックの不変性を強制することはできません)。

+0

ありがとう、トッド。私は "追記型"不変性を読むために私の質問を修正しました。 DataContractSerializerでフリーズメソッドを使用して調査します。 – dthrasher

+0

私の他の(二番目の)答えを見てください。 DataContractSerializerがプライベートメンバーをデシリアライズできる場合、 "popsicle immutability"またはFreeze()メソッドはまったく必要ないはずです。 –

2

ここでの議論をご覧ください。Why XML-Serializable class need a parameterless constructor

DataContractSerializerの使用を検討する必要があります。私がドキュメントから分かる限り、これはパラメータのないコンストラクタを必要とせず、privateメンバーを直列化/逆直列化することができます。

+0

そうです、トッド。 MSDNによると、DataContractSerializerはコンストラクターを呼び出しません。http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx – dthrasher

+0

DataContractSerializerは.NET 3.5以降でのみ使用できます。 .NET 2.0で使用することはできません – Maxence

6

クラスが本当に不変の場合は、属性がマークされたpublic readonlyフィールドを使用してください。

+0

ああ、これ!パブリックフィールド!それがプロパティを動作させるようにしようとしていた...このアンダーローブされた答えのおかげで(はい、私はそれが5年後のことを知っている) –