3

以下代码有什么问题?

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

在哪里:

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

预期:真,实际:假。

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]);
}

更新更新:这是一个微妙的错误,由列表的许多不同行为方式引起;在列表作为具有 getter 和 setter 的成员的情况下,它有一些逻辑说“如果我们得到列表,并且它不是 null,不要调用 setter - 只需添加”。但是,处理列表类型的代码意外地启用了该模式。所以发生的事情是:它会反序列化数据,然后宣布“你可以丢弃这个值”——它尽职尽责地做到了。然后“从不返回 null”代码只会创建一个新列表,然后返回。烦人的是,只有一条线来修复,而且没有副作用;固定在 r555 中。

每当有人卡住时,我都会添加一个集成测试;所以这不应该再次发生

于 2012-07-27T08:18:35.380 回答
1

我自己刚刚完成了一个项目,当时我正在使用 Protobut 并在项目中使用了大约 30 个不同的 Protocontracts 并刚刚审查了这个,因为我认为我做过类似的事情,但事实上我从未直接做过这个特殊案例。

所以我只是把它作为一个测试,但你也许可以将它用于你的目的。(是的,它是 VB,但我目前在 VB 与 C# 的对比中更快 [但我快到了])

结果不是我所期望的,所以我在这里发布了我的测试。也许您可以实现带有内部列表的类(暂时让您通过)。

Mylist ProtoBuf 错误

Mylist BinaryFormatter True

Mylist2 ProtoBuf 真

MyListI ProtoBuf 错误

MyListThing ProtoBuf 错误

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 回答