3

次のコードの何が問題になっていますか?

MyList l = Serializer.DeepClone(new MyList { "abc" });
Console.WriteLine(l.Count == 1);

どこ:

[ProtoContract]
public class MyList : List<String>
{
}

予想: true、実際: false。

4

2 に答える 2

2

すぐに修正したい場合は、[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 で修正されました。

誰かが行き詰まるたびに、統合テストを追加します。だからこんなことは二度と起きてはならない

于 2012-07-27T08:18:35.380 に答える
1

私は自分で 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
于 2012-07-26T00:21:41.863 に答える