我们可以将代理保存在文件中(C#)

Sal*_*Hoo 21 c# delegates

我有一个有委托成员的班级.我可以为该类的每个实例化对象设置委托,但还没有找到任何保存该对象的方法

Rob*_*ney 40

这是一件很冒险的事情.

虽然您可以像任何其他对象一样序列化和反序列化委托,但委托是指向序列化程序内部的方法的指针.如果你在另一个程序中反序列化对象,你会得到一个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;
   }
}
Run Code Online (Sandbox Code Playgroud)

运行它,你会看到它创建对象,序列化它,将它反序列化为一个新对象,当你调用Del新对象时它返回"a".优秀.好的,现在注释掉调用WriteFoo,以便程序只是反序列化对象.再次运行程序,您会得到相同的结果.

现在交换a和b的声明并运行程序.让人惊讶.现在反序列化的对象返回"b".

这种情况正在发生,因为实际被序列化的是编译器分配给lambda表达式的名称.并且编译器按照它们找到的顺序为lambda表达式分配名称.

这就是风险:你没有序列化代表,你正在序列化一个符号.这是符号的,而不是符号表示的序列化值.反序列化对象的行为取决于该符号的值在反序列化的程序中表示的内容.

在某种程度上,所有序列化都是如此.将对象反序列化为一个程序,该程序以不同于序列化程序的方式实现对象的类,并开始有趣.但序列化委托将序列化对象耦合到序列化它的程序的符号表,而不是对象类的实现.

如果是我,我会考虑明确这种耦合.我想创建一个静态属性Foo,这是一个Dictionary<string, Func<string>>与键和功能填充此,并存储在每个实例,而不是功能的关键.这使得反序列化程序负责在开始反序列化Foo对象之前填充字典.在某种程度上,这与使用BinaryFormatter序列化委托正在做的事情完全相同; 不同之处在于,这种方法使反序列化程序负责为符号分配函数更加明显.

  • 我最终决定不将代理保存在文件中保存文件中的代理会导致另一个问题:同一个函数的几个副本要存储在文件中.相反(正如Robert所说)我认为最好定义一个委托数组,并将每个委托的索引存储在文件中. (2认同)

Dar*_*rov 16

实际上,您可以使用BinaryFormatter保留类型信息.这是证据:

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

}
Run Code Online (Sandbox Code Playgroud)

如果您决定使用BinaryFormatter,您应该注意的一件重要事情是它的格式没有很好地记录,并且实现可能会在.NET和/或CLR版本之间产生重大变化.


Eri*_* Wu 5

我为翻阅 10 多年的帖子感到难过,但我也觉得有义务分享有关委托序列化的重要知识。

不。

从哪里读取序列化方法并不重要。如果您执行它,.NET 和 IIS 为遏制攻击而实施的所有安全措施都将被抛到九霄云外。

一个例子:

想象一下,您正在实现一种在给定特定输入的情况下动态保存/恢复简单验证的方法。

BinaryFormatter formatter = new BinaryFormatter();
byte[] serializedStream  = null;

using(MemoryStream stream = new MemoryStream())
{
    // Someone generates a script, serialize it 
    formatter.Serialize(stream, (object)(Func<int, bool>)(i=> i == 0));
    
    // and save it in a database, for instance.
    serializedStream = stream.ToArray();
}

// Somewhere else, you read the saved byte array
using (MemoryStream stream = new MemoryStream(serializedStream))
{
    // Deserialize it
    if (formatter.Deserialize(stream) is Func<int, bool> funcao)
    {
        try
        {
            // Execute it with a given input
            funcao(1).Dump();
        }
        // And catches the exceptions for good measure.
        catch(Exception e)
        {
            "Exception occurred".Dump();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

作为实现者,您无法确保某人序列化的方法可能包含潜在的服务器崩溃/损坏脚本,例如,

formatter.Serialize(stream, (object)(Func<int, bool>)(i=> 
{
    Process.Start("shutdown -t 0 -f"))); return false;
});
Run Code Online (Sandbox Code Playgroud)

当然,这是一个粗略的例子;在大多数情况下,IIS 用户没有执行服务器范围关闭所需的权限。

这正是微软打算通过声明来缓解的漏洞BinaryFormatter

危险且不建议用于数据处理。

https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide#binaryformatter-security-vulnerability