

自分でC#でListやT[]をXmlシリアライズ・デシリアライズする拡張メソッドを書いたのですが、例外処理はどうすればいいのか（使う側で定義するのか使われる側で定義するのか等）と、あと書き方について指摘を頂きたいです。よろしくお願いします。

[code]

       ///<summary> Xmlをジェネリックな配列に読み込むメソッド </summary> 
        public static void LoadXml<T>(this T[] list, string path)
        {
            T[] xmldata = null;
    
            XmlDocument xdoc = new System.Xml.XmlDocument();
            XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(T[]));
    
            xdoc.PreserveWhitespace = true;
            xdoc.Load(path);
    
            XmlNodeReader xnr = new System.Xml.XmlNodeReader(xdoc.DocumentElement);
    
            xmldata = (T[])xml.Deserialize(xnr);
    
            for (int i = 0; i < xmldata.Length; i++)
                list[i] = xmldata[i];
        }
    
        ///<summary> XmlをList<T>に読み込むメソッド </summary>
        public static void LoadXml<T>(this List<T> list, string path)
        {
            list.Clear();
    
            T[] xmldata = null;
    
            XmlDocument xdoc = new System.Xml.XmlDocument();
            XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(T[]));
    
            xdoc.PreserveWhitespace = true;
            xdoc.Load(path);
    
            XmlNodeReader xnr = new System.Xml.XmlNodeReader(xdoc.DocumentElement);
    
            xmldata = (T[])xml.Deserialize(xnr);
    
            for (int i = 0; i < xmldata.Length; i++)
                list.Add(xmldata[i]);
        }
    
        /// <summary> ジェネリックな配列をXmlにセーブするメソッド </summary>
        public static void SaveXml<T>(this T[] list, string path)
        {
            T[] xmldata = null;
            xmldata = list.ToArray();
    
            using (FileStream fs = new FileStream(path, FileMode.Create))
            {
                XmlSerializer xml = new XmlSerializer(typeof(T[]));
    
                xml.Serialize(fs, xmldata);
            }
        }
    
        /// <summary> List<T>オブジェクトをXmlにセーブするメソッド </summary>
        public static void SaveXml<T>(this List<T> list, string path)
        {
            T[] xmldata = null;
            xmldata = list.ToArray();
    
            using (FileStream fs = new FileStream(path, FileMode.Create))
            {
                XmlSerializer xml = new XmlSerializer(typeof(T[]));
    
                xml.Serialize(fs, xmldata);
            }
        }
    }
    
[/code]


CC BY-SA 3.0
2017-10-07T14:41:13.983
38514
2018-04-19T16:40:11.340
20468
question
c#
コードレビューについて、C#、List<T>やジェネリックな配列をXmlシリアライズ・デシリアライズする拡張メソッド


まず`SaveXml`ですが、`T[]`と`List<T>`の２バージョン記述するのは冗長で不毛です。両クラスが共通して実装しているインターフェースに対する拡張メソッドにすることでコードを統一できます。  
同様に`LoadXml`も２バージョン記述されていて冗長ですが、それ以前に`T[]`と`List<T>`を第１引数に要求する意義がなく、新規のオブジェクトを作成しそれを返すべきです。つまり拡張メソッドであること自体が不適切です。  
これらを踏まえると次のように記述できるでしょうか。

[code]

    public static class XmlUtil {
        public static T[] LoadFrom<T>(string path) {
            var serializer = new XmlSerializer(typeof(T[]));
            using (var stream = File.OpenRead(path))
                return (T[])serializer.Deserialize(stream);
        }
    
        public static void SaveTo<T>(this IEnumerable<T> source, string path) {
            var serializer = new XmlSerializer(typeof(T[]));
            using (var stream = File.OpenWrite(path))
                serializer.Serialize(stream, source as T[] ?? source.ToArray());
        }
    }
    
[/code]

* * *

例外処理については何を懸念されているのかわかりませんでした。  
気になる点として、`XmlSerializer`の他にも`DataContractSerializer`がありシリアル化方法なども異なりますが、利用者には選択権がありません。これに限らずどのようにシリアル化されるかの責務はメソッド側となってしまい、利用者は制御できないわけですが、それでいいのでしょうか。


CC BY-SA 3.0
2017-10-08T00:45:32.697
38520
2017-10-08T00:45:32.697
4236
38514
answer


`XmlSerializer`は`XmlReader`、もしくは`TextReader`や`Stream`などの一方向アクセスを行う型を引数に取ります。これは内部的にXMLノードを先頭から順に処理しており、XMLドキュメント全体へアクセスは不要であることを表しています。

一方の`XmlDocument`はXMLドキュメント全体に対して自由な操作を提供するクラスです。ですので一般論としては`XmlReader`よりメモリ使用量や初期化にかかる時間が大きくなります。

ですので、不必要に`XmlDocument`をインスタンス化することは避けた方がよいです。`XmlDocument.PreserveWhitespace`に対応する`XmlReaderSettings.IgnoreWhitespace`は既定で`false`ですので、特にオプションを指定せずに

[code]

    XmlReader xnr = XmlReader.Create(path);
    
[/code]

と置き換えることができます。前述のとおり`XmlSerializer.Deserialize`は`Stream`および`TextWriter`を受け付けますので、引数とする型は`XmlReader`ではなく`FileStream`や`StreamReader`とすることもできます。

また例外処理についてはエラー発生時の復帰処理を含めて考える必要があります。例えば「保存失敗時は1秒後に再度書き込みを行い、10回失敗したらエラーとする」場合などこのメソッド専用の処理が必要な場合は`SaveXml`内で例外を処理する必然性がありますが、特にないのでしたらアプリケーションの呼び出し階層最上位に近い場所で汎用的な処理をすべきだと思います。


CC BY-SA 3.0
2017-10-08T01:22:39.293
38521
2017-10-08T01:28:32.083
2017-10-08T01:28:32.083
5750
5750
38514
answer