次のコードの何が問題になっていますか?
MyList l = Serializer.DeepClone(new MyList { "abc" });
Console.WriteLine(l.Count == 1);
どこ:
[ProtoContract]
public class MyList : List<String>
{
}
予想: true、実際: false。
次のコードの何が問題になっていますか?
MyList l = Serializer.DeepClone(new MyList { "abc" });
Console.WriteLine(l.Count == 1);
どこ:
[ProtoContract]
public class MyList : List<String>
{
}
予想: true、実際: false。
すぐに修正したい場合は、[ProtoContract]
;を取り除いてください。リストには組み込みの動作があり、コントラクトとしてマークする必要はありません。
この場合、コントラクト定義がリストであるという事実よりも優先されているようです。これを変更すると、望ましくない副作用が発生するかどうかを注意深く確認する必要があります。
作業例:
public class MyList : List<string>{}
[Test]
public void ListSubclassShouldRoundTrip()
{
var list = new MyList { "abc" };
var clone = Serializer.DeepClone(list);
Assert.AreEqual(1, clone.Count);
Assert.AreEqual("abc", clone[0]);
}
現在壊れている例:
[ProtoContract]
public class MyContractList : List<string> { }
[Test]
public void ContractListSubclassShouldRoundTrip()
{
var list = new MyContractList { "abc" };
var clone = Serializer.DeepClone(list);
Assert.AreEqual(1, clone.Count);
Assert.AreEqual("abc", clone[0]);
}
アップデート; ただし、リストが型のメンバーである場合は正常に機能するため、これは機能するはずです。この問題は、リストがグラフの最も外側のタイプである場合にのみ発生します。例 (すべてのテストに合格):
[ProtoContract]
public class ListWrapper
{
[ProtoMember(1)]
public List<string> BasicList { get; set; }
[ProtoMember(2)]
public MyList MyList { get; set; }
[ProtoMember(3)]
public MyContractList MyContractList { get; set; }
}
[Test]
public void TestBasicListAsMember()
{
var obj = new ListWrapper { BasicList = new List<string> { "abc" } };
var clone = Serializer.DeepClone(obj);
Assert.IsNull(clone.MyList);
Assert.IsNull(clone.MyContractList);
Assert.AreEqual(1, clone.BasicList.Count);
Assert.AreEqual("abc", clone.BasicList[0]);
}
[Test]
public void TestMyListAsMember()
{
var obj = new ListWrapper { MyList = new MyList { "abc" } };
var clone = Serializer.DeepClone(obj);
Assert.IsNull(clone.BasicList);
Assert.IsNull(clone.MyContractList);
Assert.AreEqual(1, clone.MyList.Count);
Assert.AreEqual("abc", clone.MyList[0]);
}
[Test]
public void TestMyContractListAsMember()
{
var obj = new ListWrapper { MyContractList = new MyContractList { "abc" } };
var clone = Serializer.DeepClone(obj);
Assert.IsNull(clone.BasicList);
Assert.IsNull(clone.MyList);
Assert.AreEqual(1, clone.MyContractList.Count);
Assert.AreEqual("abc", clone.MyContractList[0]);
}
更新の更新: これは微妙なバグで、リストのさまざまな動作が原因で発生しました。ゲッターとセッターを持つメンバーとしてのリストの場合、「リストを取得し、それがnullでない場合は、セッターを呼び出さずに追加するだけです」というロジックがあります。ただし、リストである型を処理するコードは、誤ってそのモードを有効にしていました。つまり、データを逆シリアル化し、「この値を破棄できます」と宣言し、忠実に実行しました。次に、「null を返さない」コードは新しいリストを作成し、代わりにそれを返します。厄介なことに、修正するのはワンライナーであり、副作用はありません。r555 で修正されました。
誰かが行き詰まるたびに、統合テストを追加します。だからこんなことは二度と起きてはならない
私は自分で Protobut を使って作業していたプロジェクトを完成させ、プロジェクトで約 30 の異なる Protocontracts を使用し、これをレビューしました。
だから私はこれをテストとしてノックアップしましたが、あなたの目的のためにそれを使うことができるかもしれません. (ええ、それは VB ですが、私は現在、C# よりも VB の方が速くノックアップしています [しかし、私はそこに到達しています])
結果は期待したものではなかったので、ここにテストを投稿しました。もしかしたら、Class with Internal list を実装できるかもしれません (今のところは)。
Mylist ProtoBuf False
Mylist BinaryFormatter True
Mylist2 ProtoBuf True
MyListI ProtoBuf False
MyListThing ProtoBuf False
Imports System.IO
Imports ProtoBuf
Public Class Entry
Shared Sub Main()
Dim l As New MyList
l.Add("abc")
Dim newList As MyList
Using ms As New MemoryStream
Serializer.Serialize(Of MyList)(ms, l)
ms.Seek(0, SeekOrigin.Begin)
newList = Serializer.Deserialize(Of MyList)(ms)
End Using
Console.WriteLine("Mylist ProtoBuf {0}", newList.Count = 1)
Dim f As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
Using ms As New MemoryStream
f.Serialize(ms, l)
ms.Seek(0, SeekOrigin.Begin)
newList = CType(f.Deserialize(ms), MyList)
End Using
Console.WriteLine("Mylist BinaryFormatter {0}", newList.Count = 1)
Dim l2 As New MyList2
l2.Items.Add("abc")
Dim newList2 As MyList2
Using ms As New MemoryStream
Serializer.Serialize(Of MyList2)(ms, l2)
ms.Seek(0, SeekOrigin.Begin)
newList2 = Serializer.Deserialize(Of MyList2)(ms)
End Using
Console.WriteLine("Mylist2 ProtoBuf {0}", newList2.Items.Count = 1)
Dim li As New MyListI
li.Add(5)
Dim newListi As MyListI
Using ms As New MemoryStream
Serializer.Serialize(Of MyListI)(ms, li)
ms.Seek(0, SeekOrigin.Begin)
newListi = Serializer.Deserialize(Of MyListI)(ms)
End Using
Console.WriteLine("MyListI ProtoBuf {0}", newListi.Count = 1)
Dim lh As New MyListThing
lh.Add(New Thing() With {.Message = "abc"})
Dim newListh As MyListThing
Using ms As New MemoryStream
Serializer.Serialize(Of MyListThing)(ms, lh)
ms.Seek(0, SeekOrigin.Begin)
newListh = Serializer.Deserialize(Of MyListThing)(ms)
End Using
Console.WriteLine("MyListThing ProtoBuf {0}", newListh.Count = 1)
End Sub
End Class
<ProtoContract(), Serializable()>
Public Class MyList
Inherits List(Of String)
End Class
<ProtoContract()>
Public Class MyList2
Public Sub New()
Items = New List(Of String)
End Sub
<ProtoMember(1)>
Public Property Items As List(Of String)
End Class
<ProtoContract()>
Public Class MyListI
Inherits List(Of Integer)
End Class
<ProtoContract()>
Public Class MyListThing
Inherits List(Of Thing)
End Class
<ProtoContract()>
Public Class Thing
<ProtoMember(1)>
Public Property Message As String
End Class