Как реализовать ISerializable в F # - PullRequest
2 голосов
/ 23 июля 2010

Допустим, вы начинаете с этой заглушки:

[<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 # не имеет защищенного ключевого слова - так как мне это сделать?

Ограничения (из-за требования полностью соответствовать классам C # на уровне API):

  1. Долженреализовать ISerializable
  2. Конструктор должен быть защищен

РЕДАКТИРОВАТЬ - интересная дополнительная информация Спецификация F # говорит, что если вы переопределите защищенную функцию, то полученная функция будет защищена.Это неверноЕсли вы не укажете доступность, результирующее переопределение будет открытым, несмотря ни на что (разрыв контракта).

Ответы [ 3 ]

2 голосов
/ 24 июля 2010

Невозможно сделать это в настоящее время, используя язык как есть.Это можно сделать, и у меня есть два способа.

Первый - запустить сборку вывода через ILDASM, выполнить регулярное преобразование в объявление метода, которое вы хотите, изменить «public» на «family» в нужном вам методе.потом ИЛАСМ вернись.Ewwwww.

Второй, который я исследую, состоит в том, чтобы пометить методы с помощью

[<Protected>]

, а затем написать фильтр с помощью CCI , чтобы изменить доступность для всехметоды, которые имеют ProtectedAttribute и затем удаляют атрибут.Это выглядит менее неприлично, чем использование регулярных выражений над файлом, но мои настройки безопасности на работе серьезно ненавидят исходный код проекта CCI, поэтому я не могу успешно извлечь / распаковать / собрать его.

РЕДАКТИРОВАТЬ - вот мое решение- Я попробовал CCI, но он не готов к выполнению задачи.В итоге я использовал Cecil и получил следующий код:

Сначала атрибут в F #

open System

[<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 # как шаг после сборки.

1 голос
/ 24 июля 2010

фактически защищенный модификатор не является принудительным, а рекомендацией

Во время десериализации 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 голос
/ 24 июля 2010

К сожалению, нет никакого способа - F # не имеет защищенных членов. Мы рассмотрим это в следующих версиях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...