Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
168 views
in Technique[技术] by (71.8m points)

Could we save delegates in a file (C#)

I have a class which has a delegate member. I can set the delegate for each instantiated object of that class but has not found any way to save that object yet

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is a pretty risky thing to do.

While it's true that you can serialize and deserialize a delegate just like any other object, the delegate is a pointer to a method inside the program that serialized it. If you deserialize the object in another program, you'll get a SerializationException - if you're lucky.

For instance, let's modify darin's program a bit:

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;
   }
}

Run it, and you'll see that it creates the object, serializes it, deserializes it into a new object, and when you call Del on the new object it returns "a". Excellent. Okay, now comment out the call to WriteFoo, so that the program it's just deserializing the object. Run the program again and you get the same result.

Now swap the declaration of a and b and run the program. Yikes. Now the deserialized object is returning "b".

This is happening because what's actually being serialized is the name that the compiler is assigning to the lambda expression. And the compiler assigns names to lambda expressions in the order it finds them.

And that's what's risky about this: you're not serializing the delegate, you're serializing a symbol. It's the value of the symbol, and not what the symbol represents, that gets serialized. The behavior of the deserialized object depends on what the value of that symbol represents in the program that's deserializing it.

To a certain extent, this is true with all serialization. Deserialize an object into a program that implements the object's class differently than the serializing program did, and the fun begins. But serializing delegates couples the serialized object to the symbol table of the program that serialized it, not to the implementation of the object's class.

If it were me, I'd consider making this coupling explicit. I'd create a static property of Foo that was a Dictionary<string, Func<string>>, populate this with keys and functions, and store the key in each instance rather than the function. This makes the deserializing program responsible for populating the dictionary before it starts deserializing Foo objects. To an extent, this is exactly the same thing that using the BinaryFormatter to serialize a delegate is doing; the difference is that this approach makes the deserializing program's responsibility for assigning functions to the symbols a lot more apparent.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...