2010-07-23 58 views
2

比方說,你這個存根開始:如何在F#中實現了ISerializable

[<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#規範說,如果你重寫一個保護函數,結果函數將被保護。這是不正確的。如果您沒有指定可訪問性,則無論如何(違反合同),最終的覆蓋將公開。

回答

2

這是不可能做到這一點當前正在使用的語言是。這是可能的,我有兩種方法。

第一個是通過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#應用程序TEP。

1

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

1

其實保護的修飾符是不是強制,而是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} 
*) 
+1

當然,但它錯過了我完全匹配現有API的約束。 – plinth 2010-07-24 10:09:41