Невозможно сериализовать свойство в элементе управления - PullRequest
2 голосов
/ 03 октября 2008

Фон

Я пытаюсь создать копию бизнес-объекта, который я создал в VB.NET. Я реализовал интерфейс ICloneable, и в функции Clone я создаю копию объекта, сериализовав ее с помощью BinaryFormatter, а затем десериализовывая обратно в другой объект, который я возвращаю из функции.

Класс, который я пытаюсь сериализовать, помечается как «Сериализуемый» вместе с дочерними объектами, содержащимися в классе.

Я проверил, что метод клонирования работает путем написания кода, подобного следующему:

Dim obj as New Sheep()
Dim dolly as Sheep = obj.Clone()

На данный момент все работает нормально.

Задача

У меня есть пользовательский элемент управления форм Windows, который наследуется от стороннего элемента управления. Этот пользовательский элемент управления в основном содержит объект, который я хочу клонировать (поскольку этот объект ультимативно передает сторонний элемент управления).

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

Я бы хотел написать код следующего содержания:

Dim copy as Sheep = MyControl.Sheep.Clone()

Затем разрешите пользователю манипулировать свойствами на MyControl.Sheep. Однако, когда я пытаюсь сделать это, метод clone выдает исключение, заявляющее:

Тип 'MyControl' в сборке 'My_Assembly_Info_Here' не помечен как сериализуемый

Эта ошибка выдается в точке, где я звоню BinaryFormatter.Serialize(stream,Me).

Я попытался создать метод для MyControl, который возвращает копию объекта, а также сначала присвоить MyControl.Sheep другой переменной, а затем клонировать переменную, но, похоже, ничего не работает. Тем не менее, создание нового экземпляра объекта напрямую и его клонирование работает нормально!

Есть идеи, где я иду не так?

Решение

Ответ Марка помог мне направить меня в этом направлении. В этом блоге от Роки Лхотки объясняется проблема и способы ее решения.

Ответы [ 3 ]

2 голосов
/ 03 октября 2008

Есть ли у вас событие, на которое подписывается пользовательский интерфейс? {Foo} Измененное событие, если привязка данных, или, возможно, INotifyPropertyChanged? Возможно, вам придется пометить поле поддержки события как [NonSerialized] (или как бы атрибуты ни выглядели в VB - я человек C # ...). Если вы используете field-like-events (то есть сокращенный синтаксис без добавления / удаления), пометьте все событие как [field: NonSerialized] (снова переводите в VB).

Марк

0 голосов
/ 03 октября 2008

В сторонних библиотеках, если что-то не помечено как сериализуемое, оно должно не быть сериализованным по уважительной причине, но часто это не сериализуемо, потому что разработчик просто не включил его. Вы можете использовать отражение, чтобы сделать копию открытых свойств элемента управления и вернуть его состояние в отраженную версию при отмене. Этот подход может повлиять на производительность, но поскольку вы работаете на уровне пользовательского интерфейса, я думаю, что это не вызовет особого беспокойства. Этот метод не гарантирован без ошибок; публичные свойства не обязательно представляют все состояние класса, и установка некоторых свойств может иметь побочные эффекты (они не должны, но вы не написали код, так что либо ILDasm его и посмотреть, либо надеяться на лучшее).

Кроме того, не все типы свойств могут быть сериализуемыми, и в этом случае вам нужно пойти дальше, вручную написав процедуры сериализации для этих типов (и, возможно, свойств этих типов).

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    public class NonSerializableSheep
    {
        public NonSerializableSheep() { }

        public string Name { get; set; }
        public int Id { get; set; }
        // public read only properties can create a problem
        // with this approach if another property or (worse)
        // a group of properties sets it
        public int Legs { get; private set; }

        public override string ToString()
        {
            return String.Format("{0} ({1})", Name, Id);
        }
    }

    public static class GhettoSerializer
    {
        // you could make this a factory method if your type
        // has a constructor that appeals to you (i.e. default 
        // parameterless constructor)
        public static void Initialize<T>(T instance, IDictionary<string, object> values)
        {
            var props = typeof(T).GetProperties();

            // my approach does nothing to handle rare properties with array indexers
            var matches = props.Join(
                values,
                pi => pi.Name,
                kvp => kvp.Key,
                (property, kvp) =>
                    new {
                        Set = new Action<object,object,object[]>(property.SetValue), 
                        kvp.Value
                    }
            );

            foreach (var match in matches)
                match.Set(instance, match.Value, null);
        }
        public static IDictionary<string, object> Serialize<T>(T instance)
        {
            var props = typeof(T).GetProperties();

            var ret = new Dictionary<string, object>();

            foreach (var property in props)
            {
                if (!property.CanWrite || !property.CanRead)
                    continue;
                ret.Add(property.Name, property.GetValue(instance, null));
            }

            return ret;
        }
    }

    public class Program
    {
        public static void Main()
        {
            var nss = new NonSerializableSheep
            {
                Name = "Dolly",
                Id = 12
            };

            Console.WriteLine(nss);

            var bag = GhettoSerializer.Serialize(nss);

            // a factory deserializer eliminates the additional
            // declarative step
            var nssCopy = new NonSerializableSheep();
            GhettoSerializer.Initialize(nssCopy, bag);

            Console.WriteLine(nssCopy);

            Console.ReadLine();

        }
    }
}
0 голосов
/ 03 октября 2008

Очевидный вопрос, но вы уверены, что у вас нет ссылки на MyControl из вашего объекта Sheep; будь то объект или список или что-нибудь? Если это так, то это то, что мешает вам клонировать ваш бизнес-объект.

Более вероятными кандидатами будет свойство .Parent или .Tag .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...