かなり前に「CSV レコードをオブジェクトにマッピングする」というタイトルで CSV の話を書きました。実はこのときのものを RecycleBin.TextTables というライブラリーの形で NuGet で公開しています。
そしてさらに前に「インターフェイスを実装したふりをさせる」というタイトルで動的にプロキシークラスを作るという話を書きました。これもまた RecycleBin.DynamicProxy というライブラリーの形で NuGet で公開しています。
実はこれらを組合せると超強力だということに気が付いたので,ここにまとめます。
RecycleBin.TextTables
まず RecycleBin.TextTables ですが, CSV[A]を読み込む際に,以下のような感じで各レコードを任意のクラスにマッピングするというものです。
public class User { [Column("id")] public string ID { get; set; } [Column("name")] public string NickName { get; set; } [Column("join")] public DateTime JoinDate { get; set; } } using (var reader = new CsvReader("users.csv")) { var users = from user in reader.ReadToEnd<User>() ... }
類似したライブラリーに LINQtoCSV というライブラリーがありますが,こちらの方がパフォーマンスも良いし機能も豊富です。
RecycleBin.DynamicProxy
RecycleBin.DynamicProxy はサードパーティー製のクラスが特定のインターフェイスを実装してるとうれしいと思った時に,実装してもらうためのライブラリーです。
たとえば以下のようなサードーパーティー製のロギングライブラリーがあったとします。
public interface IRecordable { void AppendText(string text); } public static class Logger { public static void LogMessage(string message, IRecordable recordable) { recordable.AppendText(message); } }
そしてコンソールにログ出力をしたいと考えたとします。すると次のように書きたくなります。
Logger.LogMessage("FOO", Console.Out)
ところが Console.Out
は IRecordable
を実装していないので,コンパイルできません。こんなときに役立つのがこのライブラリーで,以下のように書くことができます。
[ProxyInterface(typeof(IRecordable))] public interface IRecordableProxy { [ProxyMethod(Target = "WriteLine", EntityType = typeof(TextWriter))] void AppendText(string text); } var builder = new DynamicProxyBuilder("LoggingProxy"); var proxy = builder.CreateProxy(typeof(IRecordableProxy), Console.Out, typeof(TextWriter)) as IRecordable; Logger.LogMessage("FOO", proxy);
やっていることは実行時にラッパークラスを作っているだけですが,これが意外と便利です。
組み合わせる
これら 2 つのライブラリーを組み合わせると, CSV レコードを自分が定義したクラスのみでなく,任意のクラスにマッピングできます。
たとえば先述の例の User
クラスがユーザー定義でないために Column
属性を付けられないとします。そのときは次のようなプロキシーを作成します。
public interface IUserProxy { [Column("id")] [ProxyMethod] public string ID { get; set; } [Column("name")] [ProxyMethod] public string NickName { get; set; } [Column("join")] [ProxyMethod] public DateTime JoinDate { get; set; } }
そして以下のような拡張メソッドを定義します。
public static class TextTableReaderExtensions { private static readonly DynamicProxyBuilder builder = new DynamicProxyBuilder("ProxyLibrary"); public static IEnumerable<T> ReadToEnd<T, TProxy>(this TextTableReader reader) { var proxyType = typeof(TProxy); while (reader.MoveNext()) { var prototype = Activator.CreateInstance<T>(); var proxy = builder.CreateProxy(proxyType, prototype); reader.Current.Convert(proxyType, ref proxy); yield return prototype; } } }
すると先述の例と同じようなことが次のようにできます。
using (var reader = new CsvReader("users.csv")) { var users = from user in reader.ReadToEnd<User, IUserProxy>() ... }
まとめ
- RecycleBin.TextTables は CSV などのテキスト形式のレコードをクラスにマッピングできる。
- RecycleBin.DynamicProxy は自分が編集できないクラスに対するメタ操作を提供する。
- これらを組み合わせると,テキスト形式のレコードを,自分が編集できないクラスであっても,マッピングできる。
いずれも自分で作ったライブラリーなのに,この強力なコンビネーションに気付かなかったなんて不覚です。ということで, CSV や類似する形式のファイルを扱う機会があったら,ぜひこれらのライブラリーをご使用ください。
脚注
- 実際は CSV 以外のテキスト形式も扱えます。 [↩]