C # Designer Проблемы с сериализацией - PullRequest
4 голосов
/ 03 марта 2009

У меня возникли проблемы с сериализацией моих объектов, и я сузил проблему до конкретного случая (см. Код ниже). Я получил следующую ошибку:

Ошибка 1 Неправильный файл Resx. Не удалось загрузить тип Serialisation.Harness.Blob, Serialization, Version = 1.0.0.0, Culture = нейтральный, PublicKeyToken = null, который используется в файле .RESX. Убедитесь, что необходимые ссылки были добавлены в ваш проект. Строка 129, позиция 5. ...

Теперь действительно странная вещь заключается в том, что перезапуск Visual Studio приводит к тому, что ошибка исчезает и код работает, но затем после, казалось бы, случайного числа сборок (во время которых указанный код не изменился), он сломается снова.

Вы видите, что я делаю неправильно / пропускаю?

Большое спасибо заранее,

Мето

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design; using System.ComponentModel.Design;

namespace Serialisation.Harness
{    

    [Serializable]
    public class Blob
    {
        public Blob()
        {
        }
    }

    [Serializable]
    public class Basic
    {

        private List<Blob> blobs;
        public List<Blob> Blobs
        {
            get { return blobs; }
            set { this.blobs= value; }
        }

        public Basic()
        {
            basics = new List<Blob>();
        }

    }

    public class BasicComponent : Component
    {

        private Basic basic = new Basic();

        private IContainer components = new Container();

        public List<Blob> Blobs
        {
            get { return basic.Blobs; }
            set { basic.Blobs= value; }
        }

        public BasicComponent(IContainer container)
        {
            container.Add(this);
        }

    }

}

1 Ответ

6 голосов
/ 03 марта 2009

Прежде всего, атрибут Serializable не используется для сериализации конструктора. При сериализации объектов конструктор сериализует файл ресурсов, когда не знает, как записать его в код конструктора. Это записывает его в resx как большой двоичный объект, используя InstanceDescriptor для конструктора типа объекта по умолчанию (при этом теряются все значения свойств, которые вы, возможно, также захотите включить). Это то, что происходит со свойством Blobs, так как дизайнер не очень хорошо сериализует универсальные списки (хотя он знает, как сериализовать массивы).

Чтобы сохранить информацию внутри этих постоянных объектов, вам нужно создать TypeConverter, который определяет другой конструктор в InstanceDescriptor (тот, который фактически принимает некоторое состояние для описания свойств, например ваше свойство Blobs). Например, если вы добавили конструктор к вашему типу BasicComponent, который принимает IEnumerable<Blob>, вы можете получить InstanceDescriptor для этого конструктора, передав массив Blob s (вы создаст новый List<Blob> вокруг этого в конструкторе). Поскольку разработчик знает, как сохранить в коде InstanceDescriptor, и поскольку он знает, как сохранить массивы в коде, он добавил бы это в код своего разработчика, а не в resx.

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

Тип преобразователя

Чтобы использовать подход с преобразователем типов, вы можете сделать что-то вроде этого:

public class BasicComponentTypeConverter : TypeConverter
{
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        bool canConvert = base.CanConvertTo(context, destinationType);

        if (!canConvert &&
            (destinationType == typeof(InstanceDescriptor))
        {
            canConvert = true;
        }

        return canConvert;
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        object conversion = null;

        if (culture == null)
        {
            culture = CultureInfo.CurrentCulture;
        }

        BasicComponent component = value as BasicComponent;
        if (basicComponent != null)
        {
            if (destinationType == typeof(InstanceDescriptor))
            {
               // Note that we convert the blobs to an array as this makes for nicer persisted code output.
               // Without it, we might just get a resource blob which is not human-readable.
               conversion = new InstanceDescriptor(
                   typeof(BasicComponent).GetConstructor(new Type[] { typeof(IEnumerable<Blob>) }),
                   new object[] { basicComponent.Blobs.ToArray() },
                   true);
            }
        }

        if (conversion == null)
        {
            conversion = base.ConvertTo(context, culture, value, destinationType);
        }

        return conversion;
    }
}

Обратите внимание, что вам может понадобиться написать конвертер типов для типа Blob. Чтобы присоединить преобразователь типа к типу, просто объявите атрибут TypeConverter в классе, который преобразователь типа будет преобразовывать, т.е. BasicConverter для приведенного выше примера.

...