【问题标题】:Is it possible to (de)serialize a private property using protobuf-net in Silverlight?是否可以在 Silverlight 中使用 protobuf-net 对私有属性进行(反)序列化?
【发布时间】: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)]。

标签: protobuf-net


【解决方案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) { }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多