簡體   English   中英

如何在F#中實現ISerializable

[英]How to implement ISerializable in F#

假設您從這個存根開始:

[<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#規范說,如果您覆蓋一個受保護的函數,則所得到的函數將受到保護。 這是不正確的。 如果您未指定可訪問性,那么無論如何(違反合同),最終的覆蓋都是公開的。

當前無法使用原樣的語言來執行此操作。 可以做到這一點,我有兩種方法。

首先是通過ILDASM運行輸出程序集,將正則表達式運行到所需的方法聲明,然后在所需的方法中將“ public”更改為“ family”,然后將其ILASM返回。 Ewwwww。

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

[<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#應用程序。

不幸的是,沒有辦法-F#沒有受保護的成員。 我們將在以后的版本中考慮這一點。

實際上,受保護修飾符不是強制執行,而是建議

在反序列化期間,使用為此目的提供的構造函數將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}
*)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM