如何序列化指向被覆盖的虚拟方法的基础的委托?

时间:2012-05-02 05:03:40

标签: .net serialization reflection delegates compact-framework

虽然other questions关于使用反射绕过所有安全性并直接调用基类的重写方法的实现,但一般都遇到了嘲笑并要求重新设计有问题的代码,我想我我们偶然发现了一个奇怪但合法的用例:delegate serialization。由于我已经看到了其他问题,不要用重新设计我的代码并停止尝试绕过类型系统的建议来轰炸我 - 我正在写一个序列化格式化程序,以及那些already get a pass to ignore constructors

令我沮丧的是,即使是v2.0 BCL的BinaryFormatter也未通过这个简单的NUnit测试:

[TestFixture]
public class DelegateSerializationTestFixture
{
    [Test]
    public void DelegateSerializationTest()
    {
        var bigKitty = new Lion();
        var kittyNoise = bigKitty.GetKittyNoiseFunc();

        Assert.AreEqual("Meow", kittyNoise());

        var stream = new MemoryStream();
        var formatter = new BinaryFormatter();
        formatter.Serialize(stream, kittyNoise);
        stream.Position = 0;
        formatter = new BinaryFormatter();
        var kittyNoise2 = (Func<string>)formatter.Deserialize(stream);

        Assert.AreEqual("Meow", kittyNoise2()); // returns Grrr
    }
}

[Serializable]
public class Lion : Cat
{
    public override string GetNoise()
    {
        return "Grrr";
    }

    public Func<string> GetKittyNoiseFunc()
    {
        return base.GetNoise;
    }
}

[Serializable]
public class Cat
{
    public virtual string GetNoise()
    {
        return "Meow";
    }
}

如果BinaryFormatter本身无法做到这一点,我想我自己的Compact Framework 3.5二进制序列化实现也不会让我感到惊讶。

如果真的不可能只使用Compact Framework v3.5的有限反射功能重建这样的委托 - 而且很可能 - 是否有办法至少检测到这样的委托,以便我的序列化程序可以抛出在流中留下不正确的数据?

到目前为止,我知道在序列化时检测此条件的唯一方法是使用完全信任反射来比较原始委托中的私有方法指针值与我公开使用它重建委托所获得的值可见的属性。

1 个答案:

答案 0 :(得分:3)

有趣的问题。我发现了一种非常 hacky检测它的方法:使用公共属性创建一个副本,并检查与原始属性的相等性。示例代码显示它正常工作:

using System;
using System.Linq;
using System.IO;

class Base
{
    public virtual void Foo()
    {
        Console.WriteLine("Base");
    }
}

class Derived : Base
{
    public override void Foo()
    {
        Console.WriteLine("Derived");
    }

    public Action NonVirtualAction { get { return base.Foo; } }
    public Action VirtualAction { get { return Foo; } }
}

class Program
{
    static void Main()
    {
        var derived = new Derived();
        var virtualAction = derived.VirtualAction;
        var nonVirtualAction = derived.NonVirtualAction;

        var virtualCopy = CreateCopy(virtualAction);
        var nonVirtualCopy = CreateCopy(nonVirtualAction);

        Console.WriteLine(virtualCopy.Equals(virtualAction)); // True
        Console.WriteLine(nonVirtualCopy.Equals(nonVirtualAction)); // False
    }

    static Delegate CreateCopy(Delegate del)
    {
        // TODO: Validate it's not multi-cast
        return Delegate.CreateDelegate(del.GetType(), del.Target, del.Method);
    }
}

我不知道你实际上是如何创建它...或者是否存在其他情况,这种“复制等同”会产生误报。