Protobuff.net 无法序列化接口

Protobuff.net can't serialise Interface

我遇到了错误

The type cannot be changed once a serializer has been generated

尝试使用 Protobuff.net 进行序列化时。我已经设法减少代码以找到罪魁祸首,但想知道为什么它无法序列化这个 属性.

我找到了一个可以使用的可行解决方案,但我对这段代码失败原因的解释很感兴趣。

不会连载:

[ProtoContract]
public class SomeController
{
    [ProtoMember(3)]
    public int ControllerValue { get; set; }

    [ProtoMember(4, AsReference = true)]
    private ITest ITestObj { get; set; }

    private SomeController(){}
    public SomeController(object something, int value)
    {
        ControllerValue = value;
        ITestObj = something as ITest;
    }
}

将连载:

错误是由SomeController.ITestObj引起的。如果我将此 class 更改为:

[ProtoContract]
public class SomeController
{
    [ProtoMember(3)]
    public int ControllerValue { get; set; }

    [ProtoMember(4, AsReference = true)]
    private TestObj OriginalObject { get; set; }

    private ITest ITestObj => OriginalObject as ITest;

    private SomeController(){}
    public SomeController(TestObj something, int value)
    {
        ControllerValue = value;
        OriginalObject = something;
    }
}

它工作正常。

工作代码:

下面是一个自包含的 HTTP 处理程序,它将 运行 此代码并重现错误:

using System.IO;
using System.Web;
using ProtoBuf;

namespace Handlers
{
    /// <summary>
    /// Summary description for Test
    /// </summary>
    public class Test : IHttpHandler
    {
        [ProtoContract]
        public class TestObj : ITest
        {
            [ProtoMember(1, AsReference = true)]
            public SomeController SomeController { get; set; }

            [ProtoMember(2)]
            public int SomeValue { get; set; }

            private TestObj(){}
            public TestObj(int something)
            {
                SomeController = new SomeController(this, something + 1);
                SomeValue = something;
            }
        }

        [ProtoContract]
        public class SomeController
        {
            [ProtoMember(3)]
            public int ControllerValue { get; set; }

            [ProtoMember(4, AsReference = true)]
            private ITest ITestObj { get; set; }

            private SomeController() { }
            public SomeController(object something, int value)
            {
                ControllerValue = value;
                ITestObj = something as ITest;
            }
        }

        [ProtoContract]
        [ProtoInclude(5, typeof(TestObj))]
        public interface ITest
        {
            [ProtoMember(6, AsReference = true)]
            SomeController SomeController { get; set; }

            [ProtoMember(7)]
            int SomeValue { get; set; }
        }


        public void ProcessRequest(HttpContext context)
        {
            var testObj = new TestObj(5);
            var serialised = Serialiser.Serialise(testObj);
            var deserialised = Serialiser.Deserialise<TestObj>(serialised);
            HttpContext.Current.Response.Write(deserialised.SomeValue + "|" + deserialised.SomeController.ControllerValue + "<br>");
        }

        protected internal class Serialiser
        {
            protected internal static byte[] Serialise<T>(T objectToSerialise)
            {
                using (var stream = new MemoryStream())
                {
                    Serializer.Serialize(stream, objectToSerialise);
                    return stream.ToArray();
                }
            }

            protected internal static T Deserialise<T>(byte[] bytes)
            {
                using (var stream = new MemoryStream(bytes))
                {
                    return Serializer.Deserialize<T>(stream);
                }
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

界面……很尴尬。好消息是你可以在代码中给它一个额外的提示(在你开始序列化之前):

Serializer.PrepareSerializer<ITest>();

如果代码能够提前更好地检测到这一点,那就太好了,但是:现在上面的内容应该有所帮助。因此,作为示例,我将这段代码放在静态类型初始值设定项中:

static Handler1()
{
    Serializer.PrepareSerializer<ITest>();
}

但它也可以进入 global.asax 或 在您开始序列化之前 发生的任何其他地方。