電子書籍の厳選無料作品が豊富!

C#を使っているのでC・C++のジャンルでいいのか分からないのですがこちらで質問させて下さい。
現在簡単な本の管理をするアプリを作っています。

プロパティをできるだけ使わずにカプセル化して作っていたのですが、肝心のデータを保存する段階で分からなくなりました。

public class Book
{
private static int _id_gen;
private int _id2;
private string _isbn;
private string _name;

public Book(string isbn, string name)
{
this._isbn = isbn;
this._name = name;
}
}

このようにコンストラクタでデータを渡して get プロパティだけあとで追加するようにしています。
こういうデータを保存する際はまとめて保存するのではなくて List<Book> なんかを foreach で回して1件ずつ保存するといいのでしょうか?
読み込む際は1件ずつ読み込んでクラスに設定していくというのが標準的な考え方ですか?
「C# XML 保存」で検索をかけると XmlSerializer でまとめて保存する事例ばかりでちょっと分からなくなってしまいました。
よろしくお願いします。

A 回答 (2件)

# 書いている間に別の方から似たようなことをすでに回答されていますが、、そのまま投稿しちゃいます。



プロパティもカプセル化手段の一つですので「プロパティをできるだけ使わずにカプセル化して」というのはちょっとよく分かりません。
読み取り専用プロパティを設けたい場合には、以下のような自動実装プロパティを読み書き別のアクセス権にすると楽です。

public string ISBN { get; private set; }


リスト状のデータをXML保存するための標準的な考え方は「用途に応じて適切な手法を用いる」です。
たとえば、出力形式は決まっておらずアプリの都合で保存/復元が出来れば良いのであればシリアライザを用いてまとめて保存しても良いでしょうし、出力するXMLのスキーマを特定の構造にしたいとか、きめ細かいエラー処理をしたいという場合には自前の保存処理を作れば良いでしょう。

シリアライザを利用する場合、XmlSerializerでもよいですけれど、個人的には DataContractSerializer をおすすめします。
privateメンバの保存/復元も出来ますし、メンバがリスト状でも大丈夫です。

こんな風にデータクラスに属性のマーキングをして、

// System.Runtime.Serialization への参照設定をしておく。
[DataContract(Namespace = "")]
public class Book
{
public Book(string isbn, string name)
{
this.ISBN = isbn;
this.Name = name;
}
[DataMember]
public string ISBN { get; private set; }
[DataMember]
public string Name { get; private set; }
}

こんな風にリストごと保存/復元をすることが出来ます。(エラー処理等は除く)
private void xmlSerialize(string filePath, List<Book> bookShelf)
{
var confDir = Path.GetDirectoryName(filePath);
Directory.CreateDirectory(confDir);

var xmlSettings = new XmlWriterSettings();
xmlSettings.Indent = true;

using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var xmlWriter = XmlTextWriter.Create(fileStream, xmlSettings))
{
var dataContractor = new DataContractSerializer(bookShelf.GetType());
dataContractor.WriteObject(xmlWriter, bookShelf);
}
}
private List<Book> xmlDeserialize(string filePath)
{
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var dataContractor = new DataContractSerializer(typeof(List<Book>));
return (List<Book>)dataContractor.ReadObject(fileStream);
}
}

注意点としては、デシリアライズ時にはコンストラクタが呼び出されないため、データクラスに以下のようなメソッドを加えてシリアライズ前後に処理を行う必要がある場合も、あるでしょう。

[OnDeserializing]
private void onDeserializing(StreamingContext context)
{
// シリアライズ直前の処理
}
[OnDeserialized]
private void onDeserialized(StreamingContext context)
{
// シリアライズ後の処理
}


自前で保存を行うならば、XDocumentを利用したLINQ to XMLによる処理をおすすめします。
以下の例では Select で射影して一括で処理していますが、エラー時に場所や値を詳細に出したい場合などは foreach で回したり要素をもっと細かく見る処理にしても良いかと思います。

private void saveLinqToXml(string filePath, List<Book> bookShelf)
{
var confDir = Path.GetDirectoryName(filePath);
Directory.CreateDirectory(confDir);

var shelfXml = new XDocument();
shelfXml.Add(
new XElement("BookShelf",
bookShelf.Select(book =>
new XElement("Book",
new XElement("ISBN", book.ISBN),
new XElement("Name", book.Name)
)
)
)
);

shelfXml.Save(filePath);
}
private List<Book> loadLinqToXml(string filePath)
{
var shelfXml = XDocument.Load(filePath);

return shelfXml.Root
.Elements("Book")
.Select(bookNode => new Book((string)bookNode.Element("ISBN"), (string)bookNode.Element("Name")))
.ToList();
}
    • good
    • 0
この回答へのお礼

プロパティもカプセル化手段の一つなのですね!
勉強不足でした。プロパティなどできるだけ外部に公開せずに設計するのが基本と思い込んでいました。
データを与えるときだけは後から変更されないようにコンストラクタで、取得はプロパティで試してみます。
普通に考えたら何のためのプロパティだって話ですもんね。
サンプルコードまで載せて頂いてありがとうございます。
DataContractSerializer 早速勉強してみます。
ありがとうございました。

お礼日時:2014/11/05 19:18

XmlSerializerやDataContractSerializerといったシリアライザを使わないのであれば,


System.Xml.Linq.XDocumentやSystem.Xml.XmlDocumentを使って一つずつ要素化していった後保存し,
デシリアライズも要素を読み取りながら一つずつ逆変換を行うのが基本です。

XmlSerializerも内部ではそのようにして書き出しています。
使っているのはXDocument/XmlDocumentではなく,XmlWriter/XmlReaderのようですが。
また,シリアライザ自体は汎用品なので,リフレクションを使ってプロパティを取得しています。


ただ,実装がまずいと,
・循環参照が処理できない
・同一のオブジェクトがシリアライズ・デシリアライズを経て別のオブジェクトになる
といったことが起きます。
なので,シリアライザを自分で書くことはあまりお薦めできません。
# 最悪,データのシリアライズ・デシリアライズ専用のクラスを作ってしまい,XmlSerializerを使うのもよいかと。
    • good
    • 0
この回答へのお礼

なるほど!やはりループで回すのが基本なのですね。
シリアライズ専用クラスは動きが理解しやすそうなので一度試してみます!
ありがとうございました!

お礼日時:2014/11/05 19:18

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!