C# &引用;没有为类型定义序列化程序:System.Collections.HashTable“;或;System.ComponentModel.ISite“;使用protobuf网络时

C# &引用;没有为类型定义序列化程序:System.Collections.HashTable“;或;System.ComponentModel.ISite“;使用protobuf网络时,c#,protobuf-net,C#,Protobuf Net,尝试在protobuf net中序列化类时遇到以下错误。哈希表包含许多不同类型的对象数组,所以很遗憾我不能使用字典。有ISite的派生类型,但如果接口本身是.NET的一部分,我所能做的就不多了 我查阅了一些关于RuntimeTypeModel的资料,但据我所知,它只适用于您创建的自定义类。任何指导都将不胜感激!谢谢。哈希表之所以不能很好地工作,是因为它不知道您要在其中放入什么。protobuf net是一个基于契约的序列化程序哈希表本质上是对象-到-对象的映射。而object是而不是合同-事实上

尝试在protobuf net中序列化类时遇到以下错误。哈希表包含许多不同类型的对象数组,所以很遗憾我不能使用字典。有ISite的派生类型,但如果接口本身是.NET的一部分,我所能做的就不多了


我查阅了一些关于RuntimeTypeModel的资料,但据我所知,它只适用于您创建的自定义类。任何指导都将不胜感激!谢谢。

哈希表之所以不能很好地工作,是因为它不知道您要在其中放入什么。protobuf net是一个基于契约的序列化程序
哈希表本质上是
对象
-到-
对象
的映射。而
object
而不是合同-事实上,它与合同完全相反。相比之下,
Dictionary
可以很好地工作(只要
SomeType
可以用作合同)

ISite
不起作用的原因可能是它是一个接口。接口代表实现,而不是数据。事实上,protobuf net对接口成员的支持是有限的,但坦率地说,最好让接口远离DTO(即数据契约)。它应该创建什么类型的ISite
实现?这需要什么数据?您使用的
SomeFunSite:ISite
是如何被实例化和连接到事物的?序列化程序涉及的问题太多。序列化程序想要的是:“我正在序列化一个
Foo
Foo
有两个整数、一个字符串和一个
Bar
——尽管在某些情况下还需要知道
SuperBar:Bar
子类。”。问题已经够多了

良好合同:

[ProtoContract]
public class MyData {
    [ProtoMember(1)]
    public Dictionary<int, SomeType> Items {get; } = new Dictionary<int, SomeType>();

    [ProtoMember(2)]
    public SomethingElse Whatever { get;set;}
}
[ProtoContract]
public class MyData {
    [ProtoMember(1)]
    public Hashtable Items {get; } = new Hashtable();

    [ProtoMember(2)]
    public ISometing Whatever { get;set;}
}
在某些情况下,可能需要配置
RuntimeTypeModel
来理解您要做的事情,但是:不总是这样。这取决于上下文;我没有的背景


编辑:次要澄清:
{get;}
-现在源代码中只支持属性,但在当前的NuGet构建中不支持属性-基本上,现在还不使用它


下面是一个标记存储类似数据的可运行示例:

using ProtoBuf;
using System;
using System.Collections.Generic;

static class Program
{
    static void Main()
    {
        var obj = new MyData
        {
            Site = new BasicSite { BaseHost = "http://somesite.org" },
            Items =
            {
                {"key 1", SomeType.Create(123) },
                {"key 2", SomeType.Create("abc") },
                {"key 3", SomeType.Create(new Whatever { Id = 456, Name = "def" }) },
            }
        };

        var clone = Serializer.DeepClone(obj);
        Console.WriteLine($"Site: {clone.Site}");
        foreach(var pair in clone.Items)
        {
            Console.WriteLine($"{pair.Key} = {pair.Value}");
        }
    }
}


[ProtoContract]
class MyData
{
    private readonly Dictionary<string, SomeType> _items
        = new Dictionary<string, SomeType>();
    [ProtoMember(1)]
    public Dictionary<string, SomeType> Items => _items;
    [ProtoMember(2)]
    public ISite Site { get; set; }
}

[ProtoContract]
[ProtoInclude(1, typeof(SomeType<int>))]
[ProtoInclude(2, typeof(SomeType<string>))]
[ProtoInclude(3, typeof(SomeType<Whatever>))]
abstract class SomeType
{
    public object Value { get { return UntypedValue; } set { UntypedValue = value; } }
    protected abstract object UntypedValue { get; set; }

    public static SomeType<T> Create<T>(T value) => new SomeType<T> { Value = value };
}
[ProtoContract]
class SomeType<T> : SomeType
{
    [ProtoMember(1)]
    public new T Value { get; set; }
    protected override object UntypedValue { get => Value; set => Value = (T)value; }
    public override string ToString() => Value?.ToString() ?? "";
}
[ProtoContract]
class Whatever
{
    [ProtoMember(1)]
    public int Id { get; set; }
    [ProtoMember(2)]
    public string Name { get; set; }
    public override string ToString() => $"{Id}, {Name}";
}
[ProtoContract]
[ProtoInclude(1, typeof(BasicSite))]
interface ISite
{
    void SomeMethod();
}
[ProtoContract]
class BasicSite : ISite
{
    void ISite.SomeMethod() { Console.WriteLine(BaseHost); }
    [ProtoMember(1)]
    public string BaseHost { get; set; }
    public override string ToString() => BaseHost;
}
使用ProtoBuf;
使用制度;
使用System.Collections.Generic;
静态类程序
{
静态void Main()
{
var obj=新的MyData
{
站点=新BasicSite{BaseHost=”http://somesite.org" },
项目=
{
{“key 1”,SomeType.Create(123)},
{“key 2”,SomeType.Create(“abc”)},
{“key 3”,SomeType.Create(新的{Id=456,Name=“def”}),
}
};
var clone=Serializer.DeepClone(obj);
Console.WriteLine($“Site:{clone.Site}”);
foreach(clone.Items中的变量对)
{
Console.WriteLine($“{pair.Key}={pair.Value}”);
}
}
}
[原始合同]
类MyData
{
专用只读字典\u项
=新字典();
[原成员(1)]
公共字典项=>\u项;
[原成员(2)]
公共ISite站点{get;set;}
}
[原始合同]
[ProtoInclude(1,typeof(SomeType))]
[ProtoInclude(2,typeof(SomeType))]
[ProtoInclude(3,typeof(SomeType))]
抽象类SomeType
{
公共对象值{get{return UntypedValue;}set{UntypedValue=Value;}}
受保护的抽象对象非类型值{get;set;}
公共静态SomeType Create(T value)=>newsometype{value=value};
}
[原始合同]
类SomeType:SomeType
{
[原成员(1)]
公共新T值{get;set;}
受保护的重写对象非类型值{get=>Value;set=>Value=(T)Value;}
公共重写字符串ToString()=>值?.ToString()?“”;
}
[原始合同]
什么课都行
{
[原成员(1)]
公共int Id{get;set;}
[原成员(2)]
公共字符串名称{get;set;}
公共重写字符串ToString()=>$“{Id},{Name}”;
}
[原始合同]
[ProtoInclude(1,类型(基硅石))]
界面云母
{
无效方法();
}
[原始合同]
基硅钙石类:硅钙石
{
void ISite.SomeMethod(){Console.WriteLine(BaseHost);}
[原成员(1)]
公共字符串BaseHost{get;set;}
公共重写字符串ToString()=>BaseHost;
}

RuntimeTypeModel适用于任何类型,而不仅仅是您创建的类。什么是
ISite
?这是一个接口吗?我不认为这会奏效。。。接口是实现,而不是数据。你能更具体地说明这是什么吗?我说的哈希表基本上是一本字典。ISite变量将是从接口派生的两种类型之一,因此我可能可以在这两种派生类型上测试它。@Varun如果2
ISite
实现非常简单,那么[ProtoInclude]可能可以使它全部工作。没有例子很难说。但是“(可能是多种相互不相关的类型)”不是一个好合同。不过,有一些方法可以在可靠的合同中体现这一点;例如,声明多个已知容器子类的公共基类。可以通过
SomeBaseClass
SomeDerivedClass
最方便地完成(并通过[ProtoInclude]声明各种
T
)。同样,需要看一个示例来说明如何解决它。@VarunSablok我在回答中添加了一个可运行的示例序列化的一个好提示是,有时序列化的对象不是应用程序其余部分处理的对象。有时,您必须构建专门为处理好序列化和反序列化而构建的类型,而这些类型的要求可能与t完全不同