Tony Pazzard Ответов: 5

Сериализация интерфейсов в C#


Ладно, я знаю, что это вполне возможно. Но.....У меня есть класс с say a List<IPerson>, где IPerson-это интерфейс. Я хочу сериализовать этот класс для хранения состояния.

Есть ли какой-нибудь простой способ, кроме реализации IXmlSerializable и выполнения всего этого самостоятельно? Это действительно кажется довольно обременительным.

Кроме того, есть ли у кого-нибудь хороший пример реализации IXmlSerializable для достижения этой цели?

Не использовать интерфейсы-это не вариант!

Что я уже пробовал:

Реализация IXmlSerializable и попадание в полный и абсолютный беспорядок!

Sergey Alexandrovich Kryukov

Конечно, вы правы, реализация IXmlSerializeble хромает. Вся идея такой сериализации нарушена.

Вот хорошая новость: вы можете забыть обо всем этом хромом. Следующий Microsoft попытка представить универсальный, ненавязчивый и абсолютно тип-агностик технологии сериализации-это гораздо лучше, чем, что. Пожалуйста, смотрите мое решение 1.

Я проголосовал за этот вопрос, потому что вы подняли критически важную проблему.

—СА

BillWoodruff

Сергей, вы действительно можете сериализовать список "экземпляров интерфейса" с помощью DataContract ... именно это и описывает здесь ОП. Если у меня будет время, я добавлю пример этого в эту тему. Вы даже можете сериализовать (значение) статического члена статического типа !

Sergey Alexandrovich Kryukov

Вы можете делать... Ну, что угодно, но это не будет сам интерфейс, вот и все. "Экземпляр интерфейса" - это то, что тип среды выполнения является классом, а не интерфейсом.
—СА

BillWoodruff

Привет Сергей, в данном случае я считаю, что ОП действительно намеревается сериализовать экземпляры интерфейса в их "интерфейсной форме", и я думаю, что предположение о том, что ОП хочет сериализовать сам фактический интерфейс, является гипотезой, для которой нет разумного основания. твое здоровье, Билл

Sergey Alexandrovich Kryukov

Тогда, скажем, я пытаюсь сделать терминологию более точной. (Все здравые гипотезы должны быть упомянуты. :-)
Я посмотрю на ваше решение.
—СА

BillWoodruff

Да, используя DataContract/DataMember, вы можете сериализовать список IPerson, где каждый IPerson в списке является результатом приведения экземпляра типа Person в его форму IPerson. Если у меня будет время, я опубликую пример этого.

5 Ответов

Рейтинг:
35

BillWoodruff

Позвольте мне привести вам рабочий пример, чтобы доказать, что экземпляры интерфейсов могут быть сериализованы. Обратите внимание, что когда я говорю "экземпляры интерфейсов", я имею в виду, что вы взяли допустимые экземпляры некоторого класса, унаследованного от интерфейса, и привели эти экземпляры в их "форму интерфейса". естественно, когда вы сохраняете некоторый "экземпляр интерфейса", подобный этому, вы не сохраняете элементы класса (поля, свойства), которые не указаны в интерфейсе.

Надуманный пример, демонстрирующий задействованные техники; чтобы сделать это "более интересным ... для меня", я также продемонстрирую, как сериализовать статический член статического класса (нет, вы не можете сериализовать сам статический класс):

namespace YourNameSpace
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization;

    public interface IAnimal
    {
        Species AnimalSpecies { set; get; }
    }

    public interface ISpecies
    {
        string SpeciesName { set; get; }
    }

    [DataContract]
    public class Species : ISpecies
    {
        [DataMember]
        public string SpeciesName { get; set; }

        public Species(string speciesName)
        {
            SpeciesName = speciesName;
        }
    }

    [DataContract]
    public class Animal : IAnimal
    {
        [DataMember]
        public Species AnimalSpecies { set; get; }

        public Animal(string speciesName)
        {
            AnimalSpecies = new Species(speciesName);
        }
    }

    [DataContract]
    public class Dog : Animal
    {
        [DataMember]
        public string DogName { set; get; }

        public Dog(string dogName, string speciesName) : base(speciesName)
        {
            DogName = dogName;
        }
    }

    [DataContract]
    public class Cat : Animal
    {
        [DataMember]
        public string CatName { set; get; }

        public Cat(string speciesName, string catName) : base(speciesName)
        {
            CatName = catName;
        }
    }

    [DataContract]
    public static class MyAnimals
    {
        [DataMember]
        public static List<Animal> TheAnimals { set; get; }

        [DataMember]
        public static List<IAnimal> TheIAnimals { set; get; }

        static MyAnimals()
        {
            TheAnimals = new List<Animal>();
            TheIAnimals = new List<IAnimal>();
        }
    }

    [DataContract]
    public class MyCats
    {
        [DataMember]
        private List<Cat> MyCatCollection { set; get; }

        public MyCats()
        {
            MyCatCollection = new List<Cat>();
        }

        public void AddCat(Cat theCat)
        {
            MyCatCollection.Add(theCat);
            MyAnimals.TheAnimals.Add(theCat);
            MyAnimals.TheIAnimals.Add(theCat);
        }
    }

    [DataContract]
    public class MyDogs
    {
        [DataMember]
        private List<Dog> MyDogCollection { set; get; }

        public MyDogs()
        {
            MyDogCollection = new List<Dog>();
        }

        public void AddDog(Dog theDog)
        {
            MyDogCollection.Add(theDog);
            MyAnimals.TheAnimals.Add(theDog);
            MyAnimals.TheIAnimals.Add(theDog);
        }
}
Обратите внимание, что каждый раз, когда вы добавляете экземпляр класса 'new Dog или Cat', их конструкторы добавляют ссылку на новый класс в две статические коллекции (универсальные списки) в статическом классе с именем ' MyAnimals. Новая собака или кошка экземпляр добавляется в список 'вид животного, но и список 'тип IAnimal.

Итак, допустим, вы создаете несколько экземпляров этой структуры классов:
// in some Method or EventHandler
MyCats myCats = new MyCats();
myCats.AddCat(new Cat("felix", "HisRoyalMeow"));
myCats.AddCat(new Cat("felix", "HerRoyalMeow"));

MyDogs myDogs = new MyDogs();
myDogs.AddDog(new Dog("Lab", "LikesToBeWet"));
myDogs.AddDog(new Dog("Chihuahua", "Mr. Sausage"));
Что вам нужно сделать, чтобы сериализовать и де-сериализовать список экземпляров Cat и Dog, хранящихся в их 'IAnimal форме в статическом классе 'MyAnimals:
public static class SaveTheAnimals
{
    private static DataContractSerializer _dcs;

    public static string filePath = @"C:\Users\YourUserName\Desktop\test\SomeAnimals.xml";

    public static void SerializeMyAnimals()
    {
         // see note below
        _dcs = new DataContractSerializer(typeof(List<IAnimal>), new List<Type>{typeof(Cat), typeof(Dog)});

        using (FileStream fs = new FileStream(filePath, FileMode.Create))
        {
            _dcs.WriteObject(fs, MyAnimals.TheIAnimals);
        }
    }

    public static List<IAnimal> DeSerializeMyAnimals()
    {
        _dcs = new DataContractSerializer(typeof(List<IAnimal>), new List<Type> { typeof(Cat), typeof(Dog) });

        List<IAnimal> myAnimals;

        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        {
            myAnimals = (List<IAnimal>)_dcs.ReadObject(fs);
        }

        return myAnimals;
    }
}
Записи:

1. Что делает эту "работу", так это предоставление новому экземпляру сериализатора DataContract списка "известных типов" для его работы. Вы заметите, что вы также можете украсить класс DataContract атрибутом 'KnownType для каждого типа, о котором вы хотите, чтобы он был "осведомлен" ... впрочем, в данном случае ... статический класс ... это не сработает (не спрашивайте меня, почему) ... и вы должны предоставить список известных типов сериализатору при его создании.

2. Посмотрите на фактический XML, полученный при сохранении списка<IAnimal>
<ArrayOfanyType xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
	<anyType i:type="a:Cat" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>felix</a:SpeciesName>
		</a:AnimalSpecies>
		<a:CatName>HisRoyalMeow</a:CatName>
	</anyType>
	<anyType i:type="a:Cat" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>felix</a:SpeciesName>
		</a:AnimalSpecies>
		<a:CatName>HerRoyalMeow</a:CatName>
	</anyType>
	<anyType i:type="a:Dog" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>LikesToBeWet</a:SpeciesName>
		</a:AnimalSpecies>
		<a:DogName>Lab</a:DogName>
	</anyType>
	<anyType i:type="a:Dog" xmlns:a="http://schemas.datacontract.org/2004/07/June6AutoForm">
		<a:AnimalSpecies>
			<a:SpeciesName>Mr. Sausage</a:SpeciesName>
		</a:AnimalSpecies>
		<a:DogName>Chihuahua</a:DogName>
	</anyType>
</ArrayOfanyType>
Обратите внимание, что каждая запись содержит не только имя вида, которое вы ожидаете, но и информацию о 'DogName или 'CatName. И, пожалуйста, спросите Microsoft, почему это работает :) потому что если вы приведете конкретную "собаку" или "кошку" к ее животной форме, вы не должны видеть эту информацию.


Рейтинг:
1

Sergey Alexandrovich Kryukov

Пожалуйста, перейдите по самый лучший и простой в использовании подхода сериализации в .Продажи, договор, сведения : Использование Контрактов Данных.

Обратите внимание, что понятие сериализации интерфейса не имеет никакого смысла. Интерфейсы не содержат данных (только метаданные).

—СА


BillWoodruff

Сергей, я приветствую Ваш отзыв о решении, которое я разместил здесь, и мои комментарии к вашему комментарию к ОП. коллегиально ваш, Билл

Рейтинг:
1

Aless Alessio

В последнее время я сталкиваюсь с одной и той же проблемой, и я пришел к этому простому, быстрому и удобочитаемому решению:

Я использую Newtonsoft.JSON сериализация/десериализация объектов отправлено в/получено из веб-служба. Если вы хотите сериализовать объекты, содержащие интерфейсы в их определении, вам просто нужно добавить свойство настроек одной строки.

Вам просто нужно добавить дополнительную информацию во время сериализация:

var indented = Formatting.Indented;
var settings = new JsonSerializerSettings()
{
      TypeNameHandling = TypeNameHandling.All
};
string serialized = JsonConvert.SerializeObject(wizardConf, indented, settings);



А затем примените ту же настройку во время десериализация:
var settings = new JsonSerializerSettings()
{
      TypeNameHandling = TypeNameHandling.All
};
YourObject obj =  JsonConvert.DeserializeObject<yourobject>(JsonString, settings);
</yourobject>



Итак, допустим, вам нужно сериализовать объект, содержащий два списка интерфейсов:
public class Class1 : IInterface1
{
   public void method1()
   {
       // whatever1
   }
}

public class Class2 : IInterface2
{
   public void method2()
   {
       // whatever2
   }
}

   public class ObjToSerialize
   {
       public List<IInterface1> list1;
       public List<IInterface2> list2;

       public ObjToSerialize()
       {
           list1 = new List<IInterface1>();
           list2 = new List<IInterface2>();
       }
   }




Предположим, что во время выполнения вы добавили по одному объекту каждый:
ObjToSerialize obj = new ObjToSerialize();
obj.list1.Add(new Class1());
obj.list2.Add(new Class2());



когда дело доходит до его сериализации, просто сделайте так, как описано выше, и результирующая строка будет выглядеть следующим образом:
{
  "$type": "Namespace.ObjToSerialize, AssemblyName",
  "list1": {
    "$type": "System.Collections.Generic.List`1[[Namespace.IInterface1, AssemblyName]], mscorlib",
    "$values": [
      {
        "$type": "Namespace.Class1, AssemblyName"
      }
    ]
  },
  "list2": {
    "$type": "System.Collections.Generic.List`1[[Namespace.IInterface2, AssemblyName]], mscorlib",
    "$values": [
      {
        "$type": "Namespace.Class2, AssemblyName"
      }
    ]
  }
}



Легко читаемый файл JSON в основном говорит все, что вам нужно знать.
В нем говорится, что ObjToSerialize содержит два свойства, каждое из которых представляет собой список интерфейсов, реализация которых Class1 в первом случае и Class2 во втором случае.

Десериализатор выше возьмет эту строку и легко восстановит объект, поскольку он имеет все детали реализации интерфейсов.


Рейтинг:
0

Bernhard Hiller

Когда-то после сериализации наступает десериализация. Поскольку вы не можете создать экземпляр интерфейса, требуется конкретный класс (реализующий этот интерфейс) - и теперь возникает трудный вопрос: какой класс вы хотите использовать здесь?
Проблема аналогична с абстрактными классами и классами, наследующими от них, например, собака и кошка наследуют от (абстрактного) животного.
В конце концов, вы можете использовать только конкретные классы для сериализации / де-сериализации. Просто представьте себе собаку, сериализованную как животное, и де-сериализованную как кошка...


BillWoodruff

Вы путаете сериализацию интерфейса ... что невозможно сделать ... с сериализацией "экземпляра интерфейса", где вы взяли экземпляр класса, который унаследовал форму интерфейса, и привели его в свою "форму интерфейса"." Это, безусловно, может быть сделано, и это почти наверняка то, что ОП хочет сделать здесь.

Рейтинг:
0

Dirk Bahle

Просто прочтите эту статью, которую я написал о сериализации интерфейсов:
Чтение и запись XML в C#/VB.Net[^]


CHill60

Этому вопросу уже больше года, и у него уже есть принятый ответ. Размещение ссылок на ваши собственные статьи часто воспринимается как РЕПО-точечное "фермерство" и может привести к понижению рейтинга

Dirk Bahle

Вы действительно заметили, что ссылка на статью актуальна?

Итак, я могу только написать статью или ответить на вопросы и ответы, но не то и другое, верно?

CHill60

Да, это так. Вот почему я прокомментировал это только для того, чтобы вы знали о восприятии. Это (или те) downvote(ы) не имеют ко мне никакого отношения.
Но общий совет был бы таков - придерживайтесь ответов на новые сообщения

Dirk Bahle

О'кей, я увидел его в списке на первой странице и подумал, что вопрос был текущим - rep-point farming-забавный термин: -) это не мое намерение, но мы можем включить любую другую ссылку на любую другую статью, если вы случайно знаете ту, которая отвечает на этот конкретный вопрос-я действительно не знаю, почему я написал эту статью в первую очередь :-)