2

背景

我正在尝试创建我在 VB.NET 中创建的业务对象的副本。我已经实现了 ICloneable 接口,并且在 Clone 函数中,我通过使用 BinaryFormatter 对其进行序列化来创建对象的副本,然后直接反序列化回从函数返回的另一个对象。

我尝试序列化的类与该类中包含的子对象一起标记为“可序列化”。

我已经通过编写类似于以下的代码测试了克隆方法的工作原理:

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

此时一切正常。

问题

我有一个继承自第 3 方控件的自定义 Windows 窗体控件。这个自定义控件基本上包含我想要克隆的对象(因为这个对象最终提供给第 3 方控件)。

我想在 windows 窗体控件中创建对象的克隆,以便我可以允许用户操作属性,同时可以选择取消更改并将对象恢复到更改之前的状态。我想在用户开始进行更改之前获取对象的副本并保留它,以便在他们按取消时准备好它。

我的想法是按照以下内容编写代码:

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

然后允许用户操作MyControl.Sheep. 但是,当我尝试这样做时,克隆方法会引发异常,说明:

程序集“My_Assembly_Info_Here”中的类型“MyControl”未标记为可序列化

在我调用BinaryFormatter.Serialize(stream,Me).

我尝试创建一个MyControl返回对象副本的方法,并首先分配MyControl.Sheep给另一个变量,然后克隆该变量,但似乎没有任何效果。但是,直接创建对象的新实例并克隆它可以正常工作!

知道我哪里出错了吗?

解决方案

马克的回答帮助我指出了正确的方向。 Rocky Lhotka 的这篇博客文章解释了这个问题以及如何解决它。

4

3 回答 3

2

你有 UI 订阅的事件吗?一个 {Foo}Changed 事件,如果是数据绑定,或者可能是 INotifyPropertyChanged?您可能必须将事件支持字段标记为 [NonSerialized] (或者属性在 VB 中看起来 - 我是 C# 人......)。如果您正在使用类似字段的事件(即没有添加/删除的缩写语法),那么用 [field: NonSerialized] 标记整个事件(同样,转换为 VB)。

于 2008-10-03T13:35:08.400 回答
0

一个明显的问题,但你确定你的 Sheep 对象没有对 MyControl 的引用吗?它是一个对象还是一个列表或任何东西?如果是这种情况,这就是阻止您克隆业务对象的原因。

更有可能的候选者是.Parent.Tag属性。

于 2008-10-03T13:14:18.473 回答
0

在第三方库中,如果某些内容未标记为可序列化,则出于充分的理由不应对其进行序列化,但通常它不可序列化,因为开发人员只是没有包含它。您可以使用反射来复制控件的公共属性,并在取消时将其状态返回到您的反射版本。这种方法对性能有影响,但因为您在 UI 层工作,我想这不会太担心。此方法不保证无错误;公共属性不一定代表一个类的整个状态,设置一些属性可能会产生副作用(它们不应该,但你没有编写代码,所以要么 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();

        }
    }
}
于 2008-10-03T13:35:46.887 回答