tags:

views:

406

answers:

3

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

+3  A: 

Actually you can with BinaryFormatter as it preserves type information. And here's the proof:

class Program
{
    [Serializable]
    public class Foo
    {
        public Func<string> Del;
    }

    static void Main(string[] args)
    {
        Foo foo = new Foo();
        foo.Del = Test;
        BinaryFormatter formatter = new BinaryFormatter();
        using (var stream = new FileStream("test.bin", FileMode.Create, FileAccess.Write, FileShare.None))
        {
            formatter.Serialize(stream, foo);
        }

        using (var stream = new FileStream("test.bin", FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            foo = (Foo)formatter.Deserialize(stream);
            Console.WriteLine(foo.Del());
        }
    }

    public static string Test()
    {
        return "test";
    }

}

An important thing you should be aware of if you decide to use BinaryFormatter is that its format is not well documented and the implementation could have breaking changes between .NET and/or CLR versions.

Darin Dimitrov
Are you sure this works when the delegate refers to a non-static method? I can see it working with static methods since no Traget need be defined, but for instance methods what does it do? Potentially, it could serialize the Target instance graph (assuming it is serializable), but then when deserialized and invoked it would be on a different instance with potentially stale data. I would personally be very careful about choosing to persist delegates in this manner as it could easily lead to some unexpected and difficult to debug/fix behaviors.
LBushkin
It works also with non-static methods. It serializes the Target instance graph as well assuming it is serializable (Marked with SerializableAttribute).
Darin Dimitrov
A: 

A delegate is a method pointer, I might misunderstand when you say save, but the location added to the delegate at runtime might not exist any longer if you try and save and restore the address.

Quintin Robinson
Thanks Quintin.You are right, as a pointer we cant. But what about their contents? anything like C++ * operator.
Sali Hoo
If you use a binary serializer, the delegate will be serialized, as well as the entire object graph that it refers to. This guarantees that the delegate can be invoked after deserialization.
Steve Guidi
A: 

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.

Robert Rossney
I finally decided Not to save delegates in filesSaving delegates in files leads to another problem:Several copies of a same function to be stored on a file. Rather (as Robert says) I think it's better to define an array of delegates, and store index of each delegate in the file.
Sali Hoo