【问题标题】:How to implement ISerializable in F#如何在 F# 中实现 ISerializable
【发布时间】:2010-07-23 14:48:25
【问题描述】:

假设您从这个存根开始:

[<Serializable>]
type Bounderizer =
val mutable _boundRect : Rectangle

new (boundRect : Rectangle) = { _boundRect = boundRect ; }
new () = { _boundRect = Rectangle(0, 0, 1, 1); }
new (info:SerializationInfo, context:StreamingContext) =
    { // to do
    }

interface ISerializable with
    member this.GetObjectData(info, context) =
        if info = null then raise(ArgumentNullException("info"))
        info.AddValue("BoundRect", this._boundRect)

    // TODO - add BoundRect property

问题是规范说,“一般来说,如果类没有密封,这个构造函数应该受到保护。” F# 没有受保护的关键字 - 那我该怎么做呢?

约束(由于要求在 API 级别完美匹配现有的 C# 类):

  1. 必须实现 ISerializable
  2. 构造函数必须受到保护

编辑 - 有趣的额外信息 F# 规范说,如果您覆盖受保护的函数,则生成的函数将受到保护。这是不正确的。如果您不指定可访问性,则生成的覆盖无论如何都会公开(违反合同)。

【问题讨论】:

    标签: .net f# constructor iserializable


    【解决方案1】:

    目前无法按原样使用该语言。有可能做到这一点,我有两种方法。

    首先是通过ILDASM运行输出程序集,正则表达式到你想要的方法声明,在你想要的方法中将'public'改为'family',然后ILASM它回来。呜呜呜。

    我正在研究的第二个方法是用

    标记方法
    [<Protected>]
    

    然后使用CCI 编写一个过滤器以更改所有方法的可访问性,而不是具有 ProtectedAttribute,然后删除该属性。这似乎比在文件上运行正则表达式不那么不合时宜,但是我在工作中的安全设置严重讨厌 CCI 项目源,因此我无法成功获取/解压缩/构建它。

    编辑 - 这是我的解决方案 - 我尝试了 CCI,但它还没有为这项任务做好准备。我最终使用Cecil 并最终得到以下代码:

    F#中的第一个属性

    打开系统

    [<AttributeUsage(AttributeTargets.Method ||| AttributeTargets.Constructor, AllowMultiple=false, Inherited=true)>]
    type MyProtectedAttribute() =
        inherit System.Attribute()
    

    然后是Cecil的客户端的以下应用程序:

    using System;
    using System.Collections.Generic;
    using System.Data.Linq;
    using System.Text;
    using Mono.Cecil;
    using Mono.Collections.Generic;
    using System.IO;
    
    namespace AddProtectedAttribute
    {
        class Program
        {
            static void Main(string[] args)
            {
                if (args.Length != 1 || args.Length != 3)
                {
                    Console.Error.WriteLine("Usage: AddProtectedAttribute assembly-file.dll /output output-file.dll");
                    return;
                }
    
                string outputFile = args.Length == 3 ? args[2] : null;
    
                ModuleDefinition module = null;
                try
                {
                    module = ModuleDefinition.ReadModule(args[0]);
                }
                catch (Exception err)
                {
                    Console.Error.WriteLine("Unable to read assembly " + args[0] + ": " + err.Message);
                    return;
                }
    
                foreach (TypeDefinition type in module.Types)
                {
                    foreach (MethodDefinition method in type.Methods)
                    {
                        int attrIndex = attributeIndex(method.CustomAttributes);
                        if (attrIndex < 0)
                            continue;
                        method.CustomAttributes.RemoveAt(attrIndex);
                        if (method.IsPublic)
                            method.IsPublic = false;
                        if (method.IsPrivate)
                            method.IsPrivate = false;
                        method.IsFamily = true;
                    }
                }
    
                if (outputFile != null)
                {
                    try
                    {
                        module.Write(outputFile);
                    }
                    catch (Exception err)
                    {
                        Console.Error.WriteLine("Unable to write to output file " + outputFile + ": " + err.Message);
                        return;
                    }
                }
                else
                {
                    outputFile = Path.GetTempFileName();
                    try
                    {
                        module.Write(outputFile);
                    }
                    catch (Exception err)
                    {
                        Console.Error.WriteLine("Unable to write to output file " + outputFile + ": " + err.Message);
                        if (File.Exists(outputFile))
                            File.Delete(outputFile);
                        return;
                    }
                    try
                    {
                        File.Copy(outputFile, args[0]);
                    }
                    catch (Exception err)
                    {
                        Console.Error.WriteLine("Unable to copy over original file " + outputFile + ": " + err.Message);
                        return;
                    }
                    finally
                    {
                        if (File.Exists(outputFile))
                            File.Delete(outputFile);
                    }
                }
            }
    
            static int attributeIndex(Collection<CustomAttribute> coll)
            {
                if (coll == null)
                    return -1;
                for (int i = 0; i < coll.Count; i++)
                {
                    CustomAttribute attr = coll[i];
                    if (attr.AttributeType.Name == "MyProtectedAttribute")
                        return i;
                }
                return -1;
            }
        }
    }
    

    最后,使用 MyProtectedAttribute 装饰您想要保护的方法,并将 C# 应用程序作为构建后步骤运行。

    【讨论】:

      【解决方案2】:

      不幸的是,没有办法 - F# 没有受保护的成员。我们将在未来的版本中考虑这一点。

      【讨论】:

        【解决方案3】:

        实际上受保护的修饰符不是强制执行,而是recommendation

        在反序列化期间,使用为此目的提供的构造函数将 SerializationInfo 传递给类。当对象被反序列化时,任何放置在构造函数上的可见性约束都会被忽略;因此您可以将类标记为公共的、受保护的、内部的或私有的。

        所以这应该有效:

        [<Serializable>]
        type Bounderizer =
            val mutable _boundRect : Rectangle
        
            new (boundRect : Rectangle) = { _boundRect = boundRect ; }
            new () = { _boundRect = Rectangle(0, 0, 1, 1); }
            private new (info:SerializationInfo, context:StreamingContext) =
                Bounderizer(info.GetValue("BoundRect", typeof<Rectangle>) :?> Rectangle)
                then
                    printfn "serialization ctor"
        
            interface ISerializable with
                member this.GetObjectData(info, context) =
                    if info = null then raise(ArgumentNullException("info"))
                    info.AddValue("BoundRect", this._boundRect)
        
            override this.ToString() = this._boundRect.ToString()
        
        let x = Bounderizer(Rectangle(10, 10, 50, 50))
        let ms = new MemoryStream()
        let f = new BinaryFormatter()
        f.Serialize(ms, x)
        ms.Position <- 0L
        let y = f.Deserialize(ms) :?> Bounderizer
        printfn "%O" y
        (*
        serialization ctor
        {X=10,Y=10,Width=50,Height=50}
        *)
        

        【讨论】:

        • 当然,但它错过了我完全匹配现有 API 的约束。
        猜你喜欢
        • 1970-01-01
        • 2018-01-21
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 2015-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多