Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Десериализовать себя? А так законно?


Автор: jonie 2.8.2008, 22:48
тут была чушь.


вопрос пусть такой будет: а как десериализовать самого себя (вариант с почленным присвоенимем не особо инетересен, мб есть что-нибудь вроде рефлексии...)?

Автор: mihryak 3.8.2008, 00:17
десериализовать из чего?
а в целом, можно и рефлексию задействовать
например, через статический фабричный метод у объекта или стандартный десериализующий конструктор

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

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;

namespace SerializationTest
{
    class Program
    {
        [Serializable]
        public class Test : ISerializable
        {
            private const BindingFlags FieldBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;

            private readonly int intValue;
            private readonly string stringValue;

            public Test(int intValue, string stringValue)
            {
                this.intValue = intValue;
                this.stringValue = stringValue;
            }

            public int IntValue
            {
                get { return intValue; }
            }

            public string StringValue
            {
                get { return stringValue; }
            }

            public override string ToString()
            {
                return String.Format("IntValue = [{0}], StringValue = [{1}]", intValue, stringValue);
            }

            #region ISerializable Members

            void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
            {
                foreach (FieldInfo fieldInfo in GetType().GetFields(FieldBindingFlags))
                {
                    info.AddValue(fieldInfo.Name, fieldInfo.GetValue(this));
                }
            }

            #endregion

            [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
            private Test(SerializationInfo info, StreamingContext context)
            {
                foreach (SerializationEntry serializationEntry in info)
                {
                    GetType().GetField(serializationEntry.Name, FieldBindingFlags).SetValue(this, serializationEntry.Value);
                }
            }

        }

        static void Main()
        {
            Test test = new Test(5, "string");
            BinaryFormatter formatter = new BinaryFormatter();
            using (MemoryStream stream = new MemoryStream())
            {
                formatter.Serialize(stream, test);
                stream.Position = 0;
                test = (Test) formatter.Deserialize(stream);
            }
            Console.WriteLine(test);
            Console.ReadKey();
        }
    }
}

Автор: jonie 3.8.2008, 00:28
до этого я дошел... 
в общем доопишу ситуацию (чего мне бы хотелось).

итак. пусть у нас есть базовый класс :
Код

class Base {
 public virtual void Serialize() {...}
 public virtual void Deserialize() {...}
}

пусть он умеет сериализоваться\десерелиазоваться кудато.

теперь я хочу чтобы и все его потомки сереализовались и десериализовались, да так, чтобы они об этом даже "не знали". Т.е. без внесения в них какого-то кода вообще.

С сериализацией все просто: метод виртуальный. Вызываем сериализацию для this. this будет указывать на потомка, и форматтер сам все сделает.

А как десериализоваться?

Автор: mihryak 3.8.2008, 01:08
ведь можно в базовом классе сделать что-то вроде моего кода - в базовом классе определить типы полей или свойств для сериализации, их вытаскивать через рефлексию
в десериализации главным тогда станет вопрос, как создать инстанс именно нужного типа (при использовании форматтера это проблема отпадает, но появляются некоторые другие smile)
при этом делать указанные методы виртуальными, наверное, не стоит, раз потомки не должны вмешиваться в процесс

и всё же - из чего и какими средствами планируется проводить (де)сериализацию?

Автор: jonie 3.8.2008, 01:17
для (де)сериализации используетя soapformatter.
виртуальность нужна для того чтобы иметь возможность писать такой код:
Код

class Base : ISerializable
{
 public virtual void Serialize()
 {
   SoapFormatter bf = new SoapFormatter();
   using (FileStream ms = new FileStream("somepath"))
   {
     SoapFormatter sf = new SoapFormatter();
     bf.Serialize(ms, this); // (!) для потомков будет сериализовываться объект потомков, т.к. функция виртуальная
   }
 }

//тоже виртуальный
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            foreach (FieldInfo fieldInfo in GetType().GetFields(FieldBindingFlags))
            {
                info.AddValue(fieldInfo.Name, fieldInfo.GetValue(this));
            }
        }
}
.....
Base o = new Deriv();
o.Serialize(); //сериализуем _потомок_, методом родителя

хочу также для десериализации 8-)
но по умолчанию Soap форматтер бросает исключение, если не находит в Deriv десериализующего конструктора (что и понятно).

вот думаю, а не написать ли свой форматтер, который будет искать конструктор и в предках, и использовать его.....

Автор: mihryak 3.8.2008, 01:44
да, десериализирующий конструктор нужен в наследниках..
но ведь и аттрубут [Serializable] тоже нужен. может, приемлимо будет принять соглашение об обязательном наличии в наследниках этого конструктора с пустым телом?
Код

            [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
            protected DerivedTest(SerializationInfo info, StreamingContext context) : base(info, context)
            {
            }


а виртуальность и правда не нужна, ведь this всегда будет правильного типа со своим собственным набором полей, так что всё будет работать
дополнил свой тестик новым классом (привожу весь код, т.к. потребовались небольшие изменения в базовом классе - см. ниже)
Код

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;

namespace SerializationTest
{
    class Program
    {
        [Serializable]
        public class Test : ISerializable
        {
            private const BindingFlags FieldBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;

            protected readonly int intValue;
            protected readonly string stringValue;

            public Test(int intValue, string stringValue)
            {
                this.intValue = intValue;
                this.stringValue = stringValue;
            }

            public int IntValue
            {
                get { return intValue; }
            }

            public string StringValue
            {
                get { return stringValue; }
            }

            public override string ToString()
            {
                return String.Format("IntValue = [{0}], StringValue = [{1}]", IntValue, StringValue);
            }

            #region ISerializable Members

            void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
            {
                foreach (FieldInfo fieldInfo in GetType().GetFields(FieldBindingFlags))
                {
                    info.AddValue(fieldInfo.Name, fieldInfo.GetValue(this));
                }
            }

            #endregion

            [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
            protected Test(SerializationInfo info, StreamingContext context)
            {
                foreach (SerializationEntry serializationEntry in info)
                {
                    GetType().GetField(serializationEntry.Name, FieldBindingFlags).SetValue(this, serializationEntry.Value);
                }
            }

        }

        [Serializable]
        public class DerivedTest : Test
        {
            private readonly double doubleValue;

            public DerivedTest(int intValue, string stringValue, double doubleValue)
                : base(intValue, stringValue)
            {
                this.doubleValue = doubleValue;
            }

            [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
            protected DerivedTest(SerializationInfo info, StreamingContext context) : base(info, context)
            {
            }

            public double DoubleValue
            {
                get { return doubleValue; }
            }

            public override string ToString()
            {
                return String.Format("IntValue = [{0}], StringValue = [{1}], DoubleValue = [{2}]", IntValue, StringValue, DoubleValue);
            }
        }

        static void Main()
        {
            Test test = new DerivedTest(5, "string", 2.2);
            BinaryFormatter formatter = new BinaryFormatter();
            using (MemoryStream stream = new MemoryStream())
            {
                formatter.Serialize(stream, test);
                stream.Position = 0;
                test = (Test) formatter.Deserialize(stream);
            }
            Console.WriteLine(test);
            Console.ReadKey();
        }
    }
}

получается, что никаких дополнительных действий по (де)сериализации отнаследованный класс не предпринимает, но он должен иметь атрибут Serializable и иметь пустой десериализующий конструктор
ещё одно ограничение - все поля, которые нужно сериализовать из базового класса, должны быть хотя бы protected, иначе GetFields(), вызванный у наследника, их попросту не вернёт

свой форматтер - это, конечно, хорошо, но если он нужен универсальный без ограничений, учесть все детали будет очень непросто, много подводных камней встретится

Автор: Idsa 3.8.2008, 08:15
 smile 
Тема топика - "сериализация самого себя" - напомнила мне фразу из одной книги по .NET:
Цитата

Teleportation in science fiction is a good example of serialization (though teleportation is not currently supported by the .NET Framework)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)