일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- c#
- github
- 다트 언어
- Data Structure
- c언어
- dart 언어
- c# 윈폼
- 깃
- Python
- Algorithm
- gitlab
- Unity
- C언어 포인터
- C# delegate
- c# winform
- 도커
- vim
- git
- 플러터
- docker
- 구조체
- jupyter
- c# 추상 클래스
- Flutter
- Houdini
- jupyter lab
- HTML
- 포인터
- C++
- 유니티
- Today
- Total
nomad-programmer
[Programming/C#] 객체 직렬화 (Serialization) 본문
BinaryWriter/Reader 와 StreamWriter/Reader는 기본 데이터 형식을 스트림에 쓰고 읽을 수 있도록 메소드를 제공한다. 하지만 프로그래머가 직접 정의한 클래스나 구조체 같은 복합 데이터 형식은 지원하지 않는다.
BinaryWriter/Reader 와 StreamWriter/Reader로 복합 데이터 형식을 기록하고 읽으려면 그 형식이 갖고 있는 필드의 값을 저장할 순서를 정한 후, 이 순서대로 저장하고 읽는 코드를 작성해야 한다.
이 문제를 위해 C#은 복합 데이터 형식을 쉽게 스트림에 읽기/쓰기를 할 수 있는 하는 "직렬화(Serialization)" 라는 메커니즘을 제공한다.
직렬화란? 객체의 상태(객체의 필드에 저장된 값들을 의미)를 메모리나 영구 저장 장치에 저장이 가능한 0과 1의 순서로 바꾸는 것을 말한다.
.NET의 직렬화가 지원하는 형식 |
.NET에서는 0과 1의 순서로 구성되는 이진(Binary) 형식으로의 직렬화도 지원하지만 JSON(JavaScript Object Notation)이나 XML같은 텍스트 형식으로의 직렬화도 지원한다. 어떤 형식이든간에 결과만 다르지 직렬화 또는 역직렬화하는 요령은 비슷하다. |
Serializable
C# 에서는 객체를 직렬화할 수 있는 아주 간단한 방법을 제공한다. 그저 [Serializable] 어트리뷰트를 클래스 선언부 앞에 붙여주면 이 클래스는 메모리나 영구 저장 장치에 저장이 가능한 형식이 된다.
[Serializable]
class MyClass
{
// ...
}
이렇게 선언한 형식은 다음과 같이 Stream 클래스와 BinaryFormatter를 이용해서 간단히 저장할 수 있다.
// 직렬화하는 예제 코드
Stream ws = new FileStream("a.dat", FileMode.Create);
BinaryFormatter serializer = new BinaryFormatter();
MyClass obj = new MyClass();
// 직렬화
serializer.Serialize(ws, obj);
ws.Close();
BinaryFormatter 클래스는 System.Runtime.Serialization.Formatter.Binary 네임스페이스에 소속되어 있고 하는 일은 객체를 직렬화(Serialization) 하거나 역직렬화(Deserialization) 하는 것이다.
// 역직렬화하는 예제 코드
Stream rs = new FileStream("a.dat", FileMode.Open);
BinaryFormatter deserializer = new BinaryFormatter();
MyClass obj = (MyClass)deserializer.Deserialize(rs);
rs.Close();
클래스 안에 어떤 필드들이 어떻게 선언되어 있는지 고민할 필요가 없다. 그냥 BinaryFormatter에게 맡기면 객체의 직렬화든 역직렬화든 알아서 해준다.
NonSerialized
상태를 저장하고 싶지 않은 필드(직렬화하고 싶지 않은 필드)가 있다면 다음과 같이 그 필드만 [NonSerialized] 어트리뷰트로 수식해주면 된다.
이렇게 하면 이 필드의 상태는 직렬화할 때도 저장되지 않고, 역직렬화할 때도 역시 복원되지 않는다.
[Serializable]
Class MyClass
{
public int myField1;
public int myField2;
// myField3을 제외한 나머지 필드들만 직렬화가된다.
[NonSerialized]
public int myField3;
public int myField4;
}
복합 데이터 형식을 직렬화할 때 주의할 점
Serializable 어트리뷰트를 이용해서 복합 데이터 형식을 직렬화하고자 할 때, 직렬화하지 않는 필드뿐 아니라 직렬화하지 못하는 필드도 Nonserializable 어트리뷰트로 수식해야 한다. 다음과 같이 Serializable하지 않은 복합 데이터 형식을 필드를 갖는 복합 데이터 형식을 직렬화하고자 하면 오류가 발생한다.
class NonseiralizableClass
{
public int myField;
}
[Serializable]
class MyClass
{
public int myField1;
public int myField2;
// NonserializableClass는 Serializable하지 않으므로 직렬화를 수행할 때 오류 발생!
public NonserializableClass myField3;
public int myField4;
}
위 코드의 문제를 해결하려면 둘 중 하나를 해야 한다.
- NonserializableClass 클래스를 Serializable 어트리뷰트로 수식한다.
- myField3을 Nonserializable 어트리뷰트로 수식한다.
Serialization & Deserialization 예제
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace CSharpExample
{
[Serializable]
class NameCard
{
public string Name;
[NonSerialized]
public string Phone;
public int Age;
}
internal class MainApp
{
static int Main(string[] args)
{
Stream ws = new FileStream("a.dat", FileMode.Create);
BinaryFormatter serializer = new BinaryFormatter();
NameCard nc = new NameCard();
nc.Name = "김연아";
nc.Phone = "010-5555-0155";
nc.Age = 31;
serializer.Serialize(ws, nc);
ws.Close();
Stream rs = new FileStream("a.dat", FileMode.Open);
BinaryFormatter deserializer = new BinaryFormatter();
NameCard nc2;
nc2 = (NameCard)deserializer.Deserialize(rs);
rs.Close();
Console.WriteLine($"Name : {nc2.Name}");
Console.WriteLine($"Phone : {nc2.Phone}");
Console.WriteLine($"Age : {nc2.Age}");
return 0;
}
}
}
/* 결과
Name : 김연아
Phone :
Age : 31
*/
List를 비롯한 컬렉션들도 직렬화를 지원한다. 예를 들어 List<NameCard> 형식의 객체도 직렬화를 통해 파일에 저장해뒀다가 이를 역직렬화를 통해 메모리 내의 컬렉션으로 불러들일 수 있다. 요령은 하나의 객체를 직렬화하고 역직렬화할 때와 동일하다.
컬렉션 직렬화 예제
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace CSharpExample
{
[Serializable]
class NameCard
{
public string Name;
public string Phone;
public int Age;
public NameCard(string Name, string Phone, int Age)
{
this.Name = Name;
this.Phone = Phone;
this.Age = Age;
}
}
internal class MainApp
{
static int Main(string[] args)
{
Stream ws = new FileStream("a.dat", FileMode.Create);
BinaryFormatter serializer = new BinaryFormatter();
List<NameCard> list = new List<NameCard>();
list.Add(new NameCard("김연아", "010-5555-0155", 31));
list.Add(new NameCard("ethan", "010-5555-5555", 35));
list.Add(new NameCard("c#", "010-1111-1111", 24));
serializer.Serialize(ws, list);
ws.Close();
Stream rs = new FileStream("a.dat", FileMode.Open);
BinaryFormatter deserializer = new BinaryFormatter();
List<NameCard> list2;
list2 = (List<NameCard>)deserializer.Deserialize(rs);
rs.Close();
foreach (NameCard nc in list2)
{
Console.WriteLine($"Name: {nc.Name}, Phone: {nc.Phone}, Age: {nc.Age}");
}
return 0;
}
}
}
/* 결과
Name: 김연아, Phone: 010-5555-0155, Age: 31
Name: ethan, Phone: 010-5555-5555, Age: 35
Name: c#, Phone: 010-1111-1111, Age: 24
*/
'Programming > C#' 카테고리의 다른 글
[Programming/C#] 스레드 임의 종료 (Abort) (0) | 2020.09.28 |
---|---|
[Programming/C#] 스레드 (Thread) (0) | 2020.09.28 |
[Programming/C#] StreamWriter / StreamReader (텍스트 파일 처리) (0) | 2020.09.28 |
[Programming/C#] BinaryWriter / BinaryReader (바이너리 파일 처리) (0) | 2020.09.28 |
[Programming/C#] 스트림 (Stream) (0) | 2020.09.28 |