C# 如何使用protobuf net反序列化需要ctor调用的自定义只读结构?

C# 如何使用protobuf net反序列化需要ctor调用的自定义只读结构?,c#,struct,protobuf-net,C#,Struct,Protobuf Net,我创建了一个自定义(只读)结构来封装十进制数。我在任何地方都使用struct,包括各种编程语言使用的面向公众的API,因此希望避免暴露十进制数据类型 这显示了结构的相关部分: [ProtoContract(SkipConstructor = false, ImplicitFields=ImplicitFields.None)] public readonly struct Amount { [ProtoIgnore] public const decimal Scale = 10

我创建了一个自定义(只读)结构来封装十进制数。我在任何地方都使用struct,包括各种编程语言使用的面向公众的API,因此希望避免暴露十进制数据类型

这显示了结构的相关部分:

[ProtoContract(SkipConstructor = false, ImplicitFields=ImplicitFields.None)]
public readonly struct Amount
{
    [ProtoIgnore]
    public const decimal Scale = 100000m;

    [ProtoIgnore]
    public decimal Value { get; }

    [ProtoMember(1)]
    public long ScaledValue { get; }

    public Amount(decimal value)
    {
        Value = value;
        ScaledValue = checked((long)(value * Scale).Round(0));
    }

    public Amount(long scaledValue)
    {
        Value = scaledValue / Scale;
        ScaledValue = scaledValue;
    }        

    public static Amount CreateFrom(long scaledValue) => new Amount(scaledValue);
}
我遇到的问题是,尽管ProtoContract上的SkipConstructor=false,但在反序列化过程中没有调用ctor,这只会导致ScaledValue属性被正确初始化

我无法使用ProtoAfterDeserialization方法设置Value属性,因为该结构是只读的

我尝试通过以下操作为protobuf net配置创建对象时使用的自定义工厂方法:

var createFrom = typeof(Amount).GetMethod("CreateFrom", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(long) }, null);
RuntimeTypeModel.Default[typeof(Amount)].SetFactory(createFrom);
但这总是导致“InvalidOperationException:由于对象的当前状态,操作无效”。我已验证是否找到CreateFrom方法(因此我将传入一个有效的MethodInfo对象)


关于如何实现这一点,我有什么想法吗?

struct
,尤其是
readonly struct
,我计划在v3中详细介绍,该版本计划使用新的序列化程序API。在此期间,它不能很好地处理这个场景,但您最好的选择可能是“代理”——这意味着序列化程序在很大程度上忽略了
Amount
,而是使用其他更便于序列化的方法。这也意味着您可以从
Amount
中删除任何序列化程序属性或API:

使用ProtoBuf;
使用ProtoBuf.Meta;
静态P类
{
静态void Main()
{
//只需要做一次,*之前*
//序列化/反序列化任何内容
RuntimeTypeModel.Default.Add(typeof(Amount),false)
.SetSurrogate(类型(数量代理));
//测试它是否有效
var obj=新的Foo{金额=新金额(1.2345亿)};
var clone=Serializer.DeepClone(obj);
System.Console.WriteLine(clone.Amount.Value);
}
}
[原始合同]
公开课Foo
{
[原成员(1)]
公共金额金额{get;set;}
}
[原始合同]
结构数量代理
{//一个很好的简单序列化类型
[原成员(1)]
公共长值{get;set;}
//运算符定义如何在这两种类型之间进行转换
公共静态隐式运算符数量(amountsubrogate值)
=>Amount.CreateFrom(value.value);
公共静态隐式运算符AmountSubrogate(金额值)
=>新数量代理{Value=Value.ScaledValue};
}

这很有效,非常感谢!(现在我所需要的就是去掉那个讨厌的包装器,所以对于面向公众的API,我可能会使用shadow属性)。