我有一堂课,有一个委托成员。我可以为该类的每个实例化对象设置委托,但尚未找到任何保存该对象的方法
这是一件非常冒险的事情。
确实可以像其他任何对象一样对委托进行序列化和反序列化,但委托是指向对其进行序列化的程序内部方法的指针。如果在另一个程序中反序列化对象,SerializationException则很幸运。
SerializationException
例如,让我们对darin的程序进行一些修改:
class Program { [Serializable] public class Foo { public Func<string> Del; } static void Main(string[] args) { Func<string> a = (() => "a"); Func<string> b = (() => "b"); Foo foo = new Foo(); foo.Del = a; WriteFoo(foo); Foo bar = ReadFoo(); Console.WriteLine(bar.Del()); Console.ReadKey(); } public static void WriteFoo(Foo foo) { BinaryFormatter formatter = new BinaryFormatter(); using (var stream = new FileStream("test.bin", FileMode.Create, FileAccess.Write, FileShare.None)) { formatter.Serialize(stream, foo); } } public static Foo ReadFoo() { Foo foo; BinaryFormatter formatter = new BinaryFormatter(); using (var stream = new FileStream("test.bin", FileMode.Open, FileAccess.Read, FileShare.Read)) { foo = (Foo)formatter.Deserialize(stream); } return foo; } }
运行它,您将看到它创建了对象,对其进行了序列化,将其反序列化为一个新对象,并在调用Del该新对象时返回“ a”。优秀的。好的,现在注释掉对的调用WriteFoo,这样程序就可以反序列化对象了。再次运行该程序,您将获得相同的结果。
Del
WriteFoo
现在交换a和b的声明并运行程序。kes。现在,反序列化的对象返回“ b”。
发生这种情况是因为实际上要序列化的是编译器分配给lambda表达式的名称。然后,编译器按照查找顺序将名称分配给lambda表达式。
这就是这样做的风险:您没有序列化委托,而是序列化了一个符号。序列化的是符号的 值 ,而不是符号所代表的含义。反序列化对象的行为取决于该符号在反序列化程序中所代表的值。
在某种程度上,所有序列化都是如此。将对象反序列化为与序列化程序不同的实现对象类的程序,然后开始有趣。但是序列化委托将序列化的对象耦合到对其进行序列化的程序的符号表,而不是对象的类的实现。
如果是我,我会考虑明确这种耦合。我将创建一个静态属性Foo为a Dictionary<string, Func<string>>,用键和函数填充此键,然后将键存储在每个实例中,而不是函数中。这使反序列化程序负责在字典开始反序列化Foo对象之前填充字典。在某种程度上,这与使用BinaryFormatter序列化委托的方式完全相同。区别在于,这种方法使反序列化程序对符号分配功能的责任更加明显。
Foo
Dictionary<string, Func<string>>
BinaryFormatter