![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?e8efa67)
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 でまとめて保存する事例ばかりでちょっと分からなくなってしまいました。
よろしくお願いします。
No.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();
}
プロパティもカプセル化手段の一つなのですね!
勉強不足でした。プロパティなどできるだけ外部に公開せずに設計するのが基本と思い込んでいました。
データを与えるときだけは後から変更されないようにコンストラクタで、取得はプロパティで試してみます。
普通に考えたら何のためのプロパティだって話ですもんね。
サンプルコードまで載せて頂いてありがとうございます。
DataContractSerializer 早速勉強してみます。
ありがとうございました。
No.1
- 回答日時:
XmlSerializerやDataContractSerializerといったシリアライザを使わないのであれば,
System.Xml.Linq.XDocumentやSystem.Xml.XmlDocumentを使って一つずつ要素化していった後保存し,
デシリアライズも要素を読み取りながら一つずつ逆変換を行うのが基本です。
XmlSerializerも内部ではそのようにして書き出しています。
使っているのはXDocument/XmlDocumentではなく,XmlWriter/XmlReaderのようですが。
また,シリアライザ自体は汎用品なので,リフレクションを使ってプロパティを取得しています。
ただ,実装がまずいと,
・循環参照が処理できない
・同一のオブジェクトがシリアライズ・デシリアライズを経て別のオブジェクトになる
といったことが起きます。
なので,シリアライザを自分で書くことはあまりお薦めできません。
# 最悪,データのシリアライズ・デシリアライズ専用のクラスを作ってしまい,XmlSerializerを使うのもよいかと。
なるほど!やはりループで回すのが基本なのですね。
シリアライズ専用クラスは動きが理解しやすそうなので一度試してみます!
ありがとうございました!
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Java JavaのSingletonパターンのprivateの持つ意味が分かりません。 5 2022/06/12 10:38
- Visual Basic(VBA) 【前回の続き続きです、ご教示ください】VBAの記述方法がわかりません。 2 2022/08/24 20:49
- Visual Basic(VBA) 【Excel VBA】自動メール送信の機能追加 5 2022/09/29 12:53
- C言語・C++・C# C++プログラミングコードにポリモーフィズムを取り入れ方を教えてください。 2 2023/06/09 11:17
- C言語・C++・C# 大量のデータを読み込んで表示する速度を改善したい 8 2023/05/07 13:29
- C言語・C++・C# C# DatagridviewにExcelシートを反映するとエラーが出る 2 2023/05/06 17:12
- PHP 配列の値の更新方法について 1 2022/08/05 09:49
- JavaScript sessionStorageを調べています。 1 2023/06/20 12:41
- Visual Basic(VBA) 【ご教示ください】VBAの記述方法がわかりません。 2 2022/08/12 21:28
- Visual Basic(VBA) 【前回の続きです、ご教示ください】VBAの記述方法がわかりません。 2 2022/08/16 16:44
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C#での関数テーブルの作り方
-
パソコンキーボードで時分秒を...
-
javaのプログラミングで作るRPG...
-
複数のテキストボックスに同じ...
-
C言語のポインターに関する警告
-
*で正三角形を出力
-
プログラミングの問題です。大...
-
JSPやサーブレットでSystem.out...
-
IF関数でEmpty値を設定する方法。
-
C言語の変数(LSB)の合わせ込...
-
論理演算子”||”またはの入力方法
-
行列の表示
-
1~100までの数字を表示し、か...
-
privateなフィールドは継承され...
-
戻り値を使用する呼出
-
n番目に大きな値を探索する
-
C#で実行時にメソッドの返り値...
-
VBAで配列の計算
-
System.err. printlnとSystem.o...
-
日付型の入力値チェック
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
特定の文字列が一致する行から...
-
C# ListBoxのアイテムをユーザ...
-
strcmp関数などでの複数の文字...
-
【Java8以降】csvファイルの複...
-
C#での関数テーブルの作り方
-
C#でIPアドレスの取得について
-
「指定されたキャストは有効で...
-
マルチスレッドで同時にFTPアッ...
-
C# JSONについて
-
Delphiで改行文字の置換がうま...
-
C#でプロパティを使わずXMLにデ...
-
C++におけるポインターと変数の...
-
jap実行時のTomcatのエラーに困...
-
C# で、あるフォルダー内にある...
-
決まった拡張子のファイルだけ...
-
strtokでの空文字への置き換え
-
c言語
-
javascript初心者
-
python文字列置換について。
-
strncpy後のatoiがおかしい
おすすめ情報