是否可以在 Silverlight 中使用 protobuf-net 对私有属性进行(反)序列化?

Posted

技术标签:

【中文标题】是否可以在 Silverlight 中使用 protobuf-net 对私有属性进行(反)序列化?【英文标题】:Is it possible to (de)serialize a private property using protobuf-net in Silverlight? 【发布时间】:2011-05-19 18:34:28 【问题描述】:

我们知道 Silverlight 不允许私有反射。不过,我有一个带有私有设置器的公共属性,我需要能够对其进行序列化(这里没问题)和反序列化(很糟糕)。

我知道世界上没有任何东西可以让 protobuf-net 在 Silverlight 中写入该属性,这必须在客户端类型(或程序集,如果该属性是内部的)中完成。

在 Silverlight 的 protobuf-net 中是否有一个计划,这使得它成为可能?我可以让该类型实现一些专门的 protobuf-net 接口(例如 IProtoSerializable)。

谢谢。

编辑

我可以提出这样的方案:

[ProtoMember(N, SetterMethod = "DeserializePropValue")]
public string property Prop

  get  return m_prop; 
  private set  m_prop = value; 


public void DeserializePropValue(ProtoValue<string> value)

  m_prop = value.Value;

类型 ProtoValue 是公共的,但它的构造函数是内部的,因此只有 protobuf-net 程序集可以创建该类型的实例。当然,protobuf-net 不会公开任何公共 API 来创建 ProtoValue 对象。

此方案仅适用于 Silverlight 平台,其他平台只会调用私有 setter。

你怎么看?

EDIT2

我想指出,当然仍然可以获得对任意 PropValue 实例的引用,但这不会是偶然的,这些是我希望消除的属性的意外覆盖。另外,我想保持 setter 不公开,这样它就不会出现在 UI 中使用的各种基于反射的绑定机制中。

EDIT3

可以使 PropValue 实例不适合存储,这意味着在 DeserializePropValue 方法返回后,相应的 PropValue 实例将失效。这样就只剩下一种滥用它的方法了,就像这样:

[ProtoContract]
public class Abusee

    [ProtoMember(1, SetterMethod = "DeserializePropValue")]
    public string property Prop  get; private set; 

    public void DeserializePropValue(ProtoValue<string> value)
    
      m_prop = value.Value;
    


[ProtoContract]
public class Abuser

  private Abusee m_abusee;

  public Abuser(Abusee abusee, string newPropValue)
  
    m_abusee = abusee;
    Dummy = newPropValue;
    Serializer.DeepClone(this);
  

  [ProtoMember(1, SetterMethod = "DeserializeDummyValue")]
  public string property Dummy
  
    get;
    private set;
  

  public void DeserializeDummyValue(ProtoValue<string> value)
  
    m_abusee.DeserializePropValue(value);
  

相当多的努力都是偶然发生的。至于故意滥用 - 这里没有回归。人们总是可以序列化一个对象,操作二进制序列化数据,然后将其反序列化。回归只是在易于滥用。但是,我的目标是:

防止意外出错 保持 setter 不公开 避免与代理相关的维护噩梦。

【问题讨论】:

我仍在阅读编辑内容,但重新考虑了反射/绑定 API - 只需添加 [ReadOnly(true)] (msdn.microsoft.com/en-us/library/…) 我还没有尝试过代码,但我想知道是否只有一个 method(而不是属性)来设置值可以让我们充分地到达那里。即[Obsolete("Use this and I kill you")] public void SetProp(string value) ...(也许还有一些东西告诉 protobuf-net 使用这种方法,但它也可以是基于模式的) 当然,我同意。我只想要一个阴影设置器方法。 @mark 酷;我可以做到 :) 我担心的是我已经非常努力允许使用不想了解 protobuf 详细信息的类型(朴素的 POCO 等) - 所以制作 protobuf 类型的一部分API 是不可取的。如果您对采用字符串的方法感到满意,那几乎没有任何工作。给我几天? (管道中的其他东西等) 使用 ObsoleteAttribute 时,还可以使用带布尔值的重载。当这个bool为真时,使用该方法时会出现编译错误。我在应仅由序列化程序使用的无参数构造函数上使用 [Obsolete("only for serialization", true)]。 【参考方案1】:

有趣的问题。 在 v2 中有一个“代理”的概念,它是为不可变对象(和结构)设计的,它可能有用 - 它取决于对象的复杂程度以及选项的吸引力有多大.第二种选择可能是使属性显示popsicle 不变性。我将说明两者,但是:

在这种情况下冰棒不变性只是半冰棒,很容易被绕过;此选项很方便,如果您只是想防止意外损坏,可能会很有用 真正的不变性给你更强的保护;本质上,代理充当“构建器”,但绝不允许您改变现有实例

请注意,目前不能在属性上指定代理(我可能会在稍后添加;p) - 所以我使用运行时模型来演示这一点:

class Program

    static void Main()
    
        var obj = new Popsicle(3, 4);
        var clone = Serializer.DeepClone(obj);
        Debug.Assert(clone.Foo == obj.Foo);
        Debug.Assert(clone.Bar == obj.Bar);

        var model = TypeModel.Create();
        model.Add(typeof(MutableSurrogate), false).Add("Foo", "Bar");
        model.Add(typeof(ImmutableType), false).SetSurrogate(typeof(MutableSurrogate));
        // note you should re-use models (cache them) - or better: pre-generate a serializer dll
        var obj2 = new ImmutableType(5, 6);
        var clone2 = (ImmutableType)model.DeepClone(obj2);
        Debug.Assert(clone2.Foo == obj2.Foo);
        Debug.Assert(clone2.Bar == obj2.Bar);
    


[ProtoContract] // should also work with DataContract etc
public class Popsicle

    public Popsicle()  
    public Popsicle(int foo, int bar)
    
        Foo = foo;
        this.bar = bar;
    
    private int isBeingDeserialized;

    [ProtoBeforeDeserialization]
    public void BeforeDeserialization()
    
        isBeingDeserialized++;
    
    [ProtoAfterDeserialization]
    public void AfterDeserialization()
    
        isBeingDeserialized--;
    
    [ProtoMember(1)]
    public int Foo  get; set;  //  fully mutable

    private int bar;
    [ProtoMember(2)]
    public int Bar
    
        get  return bar; 
        set
        
            if (bar == value) return;
            if (isBeingDeserialized <= 0) throw new InvalidOperationException();
            bar = value;
        
    


public class ImmutableType

    private readonly int foo, bar;
    public ImmutableType(int foo, int bar)
    
        this.foo = foo;
        this.bar = bar;
    
    public int Foo  get  return foo;  
    public int Bar  get  return bar;  

public class MutableSurrogate

    public static implicit operator ImmutableType(MutableSurrogate surrogate)
                
        return surrogate == null ? null
            : new ImmutableType(surrogate.Foo, surrogate.Bar);
    
    public static implicit operator MutableSurrogate(ImmutableType surrogate)
    
        return surrogate == null ? null
            : new MutableSurrogate  Foo = surrogate.Foo, Bar = surrogate.Bar ;
    
    public int Foo  get; set; 
    public int Bar  get; set; 

我目前没有IProtoSerializable 之类的东西。我还在等待找到真正需要它的东西……我不确定是不是它。

【讨论】:

我真的不喜欢冰棒的方法。但是,对于代理方法,我有一些不明白的地方。如果我有一个具有 10 个属性的类型,其中只有一个是只读的,我是否必须编写一个包含所有 10 个属性的代理,只是因为其中只有一个是只读的?似乎是一场维护噩梦。顺便说一句,你能解释一下是什么让你拒绝了我的提议吗? @mark - 在某种程度上,我不确定这给了你什么。例如,它看起来很容易被滥用。重新驳回它 - 恰恰相反,如果我们可以在这里添加一些有助于而不是漏洞的东西,我会全力以赴。不过,我的目标是提供示例,说明现在,今天可以作为起点。 我已编辑我的问题以解释我对滥用问题的看法。 在您的第二个隐式运算符定义中,将参数命名为“代理”非常令人困惑。还是我误解了什么?【参考方案2】:

看起来像 Jackson - Java Json 序列化器很好地解决了不可变类问题:http://www.cowtowncoder.com/blog/archives/2010/08/entry_409.html

基本上在工厂/构造方法上注解,并将其输入参数映射到不可变/只读属性。

【讨论】:

我不确定如何弥合 java+json 和 silverlight+protobuf 之间的差距。你如何比较靴子和苹果? 在我看来,这个想法看起来很有希望。完全不变性,然后注释构造函数参数以允许反序列化,如下所示:private ImmutableType([ProtoMemberReference(nameof(this.ReadOnlyInt))] int readOnlyInt)

以上是关于是否可以在 Silverlight 中使用 protobuf-net 对私有属性进行(反)序列化?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 Silverlight 电子邮件上创建电子邮件附件?

在 Silverlight 中获取当前 Windows 用户名

Silverlight 财务数据

是否可以从 WebPart 获得对 silverlight 页面的引用?

是否有可能弄清楚IE是否从C#或PowerShell代码重新下载Silverlight xaps?

如何使用WPF或Silverlight制作游戏?