Сериализация C# DataContract, как десериализовать к уже существующему экземпляру

У меня есть класс, который содержит статический словарь всех существующих экземпляров, которые определяются во время компиляции.

В основном это похоже на это:

[DataContract]
class Foo
{
  private static Dictionary<long, Foo> instances = new Dictionary<long, Foo>();

  [DataMember]
  private long id;

  public static readonly Foo A = Create(1);
  public static readonly Foo B = Create(2);
  public static readonly Foo C = Create(3);

  private static Foo Create(long id)
  {
    Foo instance = new Foo();
    instance.id = id;
    instances.Add(instance);
    return instance;
  }

  public static Foo Get(long id)
  {
    return instances[id];
  }    

}

Существуют другие поля, и класс получен, но это не имеет значения для проблемы.

Только id сериализируется. Когда экземпляр этого типа десериализовывается, я хотел бы получить экземпляр, который был создан как статическое поле (A, B или C), использование Foo.Get(id) вместо того, чтобы получить новый экземпляр.

Существует ли простой способ сделать это? Я не нашел ресурсов, которые я смог понять.

8
задан Stefan Steinegger 21 December 2009 в 17:50
поделиться

3 ответа

Во время десериализации он (AFAIK) всегда использует новый объект (FormatterServices.GetUninitializedObject), но чтобы заставить его заменить объекты после десериализации (но до того, как они будут возвращены вызывающему абоненту), можно реализовать IObjectReference, например:

[DataContract]
class Foo : IObjectReference { // <===== implement an extra interface
    object IObjectReference.GetRealObject(StreamingContext ctx) {
        return Get(id);
    }
    ...snip
}

done. ...доказательство:

static class Program {
    static void Main() {
        Foo foo = Foo.Get(2), clone;
        DataContractSerializer ser = new DataContractSerializer(typeof(Foo));
        using (MemoryStream ms = new MemoryStream()) { // clone it via DCS
            ser.WriteObject(ms, foo);
            ms.Position = 0;
            clone = (Foo)ser.ReadObject(ms);
        }
        Console.WriteLine(ReferenceEquals(foo, clone)); // true
    }
}

Обратите внимание, что для сценариев частичного доверия на MSDN есть дополнительные примечания здесь.

17
ответ дан 5 December 2019 в 08:24
поделиться

У меня была аналогичная проблема, и лучшим решением, которое я нашел, было добавление некоего класса-оболочки, который управлял экземплярами того, который нужно было сериализовать.

Я не уверен насчет точного подпись с Договорами. Я использовал SerializableAttribute, и с его помощью я посмотрел что-л. вот так:

[Serializable]
class FooSerializableWrapper : ISerializable
{
    private readonly long id;

    public Foo Foo
    {
        get
        {
            return Foo.Get(id);
        }
    }

    public FooSerializableWrapper(Foo foo)
    {
        id = foo.id;
    }

    protected FooSerializableWrapper(SerializationInfo info, StreamingContext context)
    {
        id = info.GetInt64("id");
    }


    void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("id", id);
    }

}
3
ответ дан 5 December 2019 в 08:24
поделиться

Вы можете сделать шаг навстречу тому, что ищете, используя OnDeserializingAttribute . Однако это, вероятно, позволит вам установить только свойства (чтобы вы могли иметь то, что составляет метод Copy, который заполняет все свойства текущего экземпляра с помощью вашего статического экземпляра.

Я думаю, если вы действительно хотите вернуть свои статические экземпляры, вам, вероятно, придется написать свой собственный десериализатор ...

Непроверено, но я предполагаю, что вы можете довольно легко реализовать десериализатор, например, следующим образом:

public class MyDeserializer : System.Xml.Serialization.XmlSerializer
{
    protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader)
    {
        Foo obj = (Foo)base.Deserialize(reader);
        return Foo.Get(obj.id);
    }
}

Обратите внимание, что вам придется что-то делать с получением идентификатора, поскольку он является закрытым в вашем коде; также предполагается, что вы используете сериализацию XML; Замените наследование тем, что вы действительно используете. И, наконец, это означает, что вы

0
ответ дан 5 December 2019 в 08:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: