简体   繁体   English

如何在F#中实现ISerializable

[英]How to implement ISerializable in F#

Let's say you start off with this stub: 假设您从这个存根开始:

[<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

The issue is that the spec says, "In general, this constructor should be protected if the class is not sealed." 问题在于规范说:“通常,如果未密封类,则应该保护此构造方法。” F# doesn't have a protected keyword - so how do I do this? F#没有受保护的关键字-那么我该怎么做?

Constraints (due to a requirement to match perfectly existing C# classes at the API level): 约束(由于要求在API级别匹配完全存在的C#类):

  1. Must implement ISerializable 必须实现ISerializable
  2. Constructor must be protected 构造函数必须受到保护

EDIT - interesting extra information The F# spec says that if you override a protected function that the resulting function will be protected. 编辑-有趣的额外信息F#规范说,如果您覆盖一个受保护的函数,则所得到的函数将受到保护。 This is incorrect. 这是不正确的。 If you don't specify accessibility, the resulting override will be public no matter what (breaking the contract). 如果您未指定可访问性,那么无论如何(违反合同),最终的覆盖都是公开的。

It is not possible to do this currently using the language as is. 当前无法使用原样的语言来执行此操作。 It is possible to do this and I have two ways. 可以做到这一点,我有两种方法。

The first is to run the output assembly through ILDASM, regex to the method declaration you want, change 'public' to 'family' in the method you want, then ILASM it back. 首先是通过ILDASM运行输出程序集,将正则表达式运行到所需的方法声明,然后在所需的方法中将“ public”更改为“ family”,然后将其ILASM返回。 Ewwwww. Ewwwww。

The second, which I'm investigating, is to tag the methods with 我正在研究的第二个方法是使用

[<Protected>]

then write a filter with CCI to change the accessibility on all methods than have the ProtectedAttribute and then remove the attribute. 然后使用CCI编写一个过滤器,以更改具有ProtectedAttribute的所有方法的可访问性,然后删除该属性。 This seems less unseemly than running a regex over the file, but my security settings at work seriously hates the CCI project source, so I can't successfully fetch/decompress/built it. 这似乎比在文件上运行正则表达式似乎不那么糟糕,但是我在工作中的安全设置严重讨厌CCI项目源,因此我无法成功获取/解压缩/构建它。

EDIT - Here is my solution - I tried CCI, but it's not ready for the task. 编辑-这是我的解决方案-我尝试过CCI,但尚未完成任务。 I ended up using Cecil and ended up with the following code: 我最终使用了Cecil,并得到了以下代码:

First an attribute in F# 首先是F#中的属性

open System 开放系统

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

then the following app which is a client of Cecil : 然后是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;
        }
    }
}

finally, decorate the methods you want to be protected with MyProtectedAttribute and run the C# app as a post-build step. 最后,装饰要使用MyProtectedAttribute保护的方法,并在构建后步骤中运行C#应用程序。

Unfortunately, there is no way - F# does not have protected members. 不幸的是,没有办法-F#没有受保护的成员。 We will consider this in future versions. 我们将在以后的版本中考虑这一点。

in fact protected modifier is not enforcement, but a recommendation 实际上,受保护修饰符不是强制执行,而是建议

During deserialization, SerializationInfo is passed to the class using the constructor provided for this purpose. 在反序列化期间,使用为此目的提供的构造函数将SerializationInfo传递给类。 Any visibility constraints placed on the constructor are ignored when the object is deserialized; 反序列化对象时,放置在构造函数上的所有可见性约束都将被忽略; so you can mark the class as public, protected, internal, or private. 因此您可以将该类别标记为公共,受保护,内部或私有。

So this should work: 所以这应该工作:

[<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}
*)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM