2014年12月21日日曜日

大河ドラマ『軍師官兵衛』最終回

今年の大河は面白かった。合戦のシーンがもう少し欲しかったかな。とか、鳥取城攻めがまるっとなかったりはちょっと残念でしたが。

来年はどうかな。再来年は今から楽しみ。

2014年10月27日月曜日

KeyedCollectionクラス

.netでコレクションというと、ほとんどのケースでListとDictionaryがあれば事足りてしまう印象があります。

とはいえ、標準のクラスライブラリには、かなりたくさんのバリエーションがあり、多くはほとんど使われていないんじゃないかと思います。(System.Collection名前空間に属するものは使わないほうがよいですが。)

そんな中で、System.Collection.ObjectModel名前空間に属するものに、たまに使いたくなるものがあったりします。

そのうちの一つに、「KeyedCollection」クラスがあります。これは、ListとDictionaryの両方の特徴を持つようなクラスになっていて、

  • Dictionaryのようにキーで要素にアクセスできる。
  • Listのように追加/挿入時の順番が保持され、Indexでアクセスできる。
  • 要素の一部がキーとして扱われる。
  • キーの重複は不可。
といった特徴を有します。なので、たとえばEntityの中にIDのような一意な値を持っていて、そのキーでアクセスするようなケースで有用です。さらに順番を保持させたいなら積極的に使うべきクラスかも。

ただ、大変惜しむらくは、KeyedCollectionクラスは抽象仮想クラスになっていて、中に収めるEntityごとにクラスを派生させて使う前提になってしまっています。因みに、派生クラスでオーバーロードするメソッドは基本的に一つのみで、Entityからキーを選びだすセレクタの「GetKeyForItem」メソッド。

使おうとするごとにクラスを一つ作らなければならず、Entityが異なればそれぞれに派生クラスを作らなければならない。今どきこれは大変めんどくさい。

なので、一回汎用的なクラスを作ったら、それを使いまわせるようにしておきたいですね。

戦略としては、「セレクタメソッドをFuncで渡すコンストラクタ」を用意し、先の「GetKeyForItem」メソッドではコンストラクタで渡されたFuncを使う。ことにすれば使いやすくなりそうです。

で、書いてみたのがこれ。

internal class SelectableKeyedCollection<TKey, TItem>
    : KeyedCollection<TKey, TItem>
{
  private Func<TItem, TKey> keySelector;

  internal SelectableKeyedCollection(Func<TItem, TKey> keySelector)
  {
    this.keySelector = keySelector;
  }

  protected override TKey GetKeyForItem(TItem item)
  {
    return keySelector(item);
  }
}

public static class KeyedCollection
{
  public static KeyedCollection<TKey, TItem>
        Create<TKey, TItem>(Func<TItem, TKey> keySelector)
  {
    return new SelectableKeyedCollection<TKey, TItem>(keySelector);
  }
}

結局2つのクラスになっていますが、下のKeyedCollectionクラスは、上のSelectableKeyedCollectionを作るだけのクラスです。そしてSelectableKeyedCollectionクラスはinternal指定としています。

なぜにこんな2段階の作りになっているかというと、

var collection = new SelectableKeyedCollection<X, Y>(x => x.y);

と書かせるより、

var collection = KeyedCollection.Create((X x) => x.y);

のほうがタイプ量も少ないし、Intellisenseもある程度効いてくれます。そして、SelectableKeyedCollectionクラスをintenalとすることで、使う人はその存在を知る必要がなくなり、KeyedCollection<TKey, TItem>を使うために、KeyedCollectionクラスさえ知っていれば良くなります。そのほうが親切だと思うのです。

.net FrameworkのTupleが同じような考え方になっています。

このクラスを使ったサンプルを書いてみます。ここでは、「あす以降の一週間のDateTimeをコレクションに突っ込み、曜日でのアクセスとシーケンシャルなアクセスを行う。」コンソールアプリケーションのサンプルです。なんかもう少し意味のあるサンプルにしたいところですが、それをやると簡潔に書けないので。

「System.Collection.ObjectModel」名前空間と、先のSelectableKeyedCollectionとKeyedCollectionが属する名前空間をusingした上で、

static void Main(string[] args)
{
  var week = KeyedCollection.Create((DateTime dt) => dt.DayOfWeek);

  foreach (var n in Enumerable.Range(1, 7))
  {
    week.Add(DateTime.Today.AddDays(n));
  }

  Console.WriteLine("Today is {0:yyyy/MM/dd (ddd)}", DateTime.Today);
  Console.WriteLine();
  Console.WriteLine("Next 7 days...");

  foreach (var d in week)
  {
    Console.WriteLine(d.ToShortDateString());
  }

  Console.WriteLine();
  Console.WriteLine(
    "Next Friday is {0:yyyy/MM/dd (ddd)}", week[DayOfWeek.Friday]);
}

これをコンパイルして実行すると、以下の結果となります。(ちなみに今日は2014年10月27日)

Today is 2014/10/27 (月)

Next 7 days...
2014/10/28
2014/10/29
2014/10/30
2014/10/31
2014/11/01
2014/11/02
2014/11/03

Next Friday is 2014/10/31 (金)

個人的には、これなら使う気になります。

もし本当に汎用的なクラスに仕上げるなら、KeyedCollection<TKey, TItem>クラスには、コンストラクタがもう2つほどあるので、それらに対する備えをしておけば使い勝手の良いクラスができると思います。

2014年9月11日木曜日

文字列からファイル名に使えない文字を(取り除く|置き換える)

LINQの小ネタをひとつ。

ファイルを作るときのファイル名に、何らかの理由でファイル名として使えない文字が含まれていないかをチェックして、含まれているときには取り除いたり、置き換えたりして処理を続行する。というケースがままあります。

この手の細かい処理が結構見落としがちで、面倒だったりします。

これをLINQを使ってお気楽実装してみます。まぁ、見落としを防ぐことはできませんけどね。

結局のところ、入力された文字列を一文字ずつ見ていって、ファイル名として使えない文字なら取り除いたり、置き換えたりする処理を実装します。「一文字ずつ」見ていくので、LINQの出番ですね。

置き換える(ここではアンダースコア'_')なら、こうですかね。
var invalidChars = Path.GetInvalidFileNameChars();
var converted = string.Concat(
  original.Select(c => invalidChars.Contains(c) ? '_' : c));

で、取り除くならこう。
var invalidChars = Path.GetInvalidFileNameChars();
var removed = string.Concat(original.Where(c => !invalidChars.Contains(c)));


お気楽でしょ。

せめて「細々と」

ご無沙汰しておりました。。。

えー、最後に(まともに内容のある)エントリを立てたのが去年の8月だったので、すでに1年以上前のことだったりします。ひえー。

なんやかや忙しかったりしたのが、少し落ち着いてきたので、せめて「細々と続いてる」状態を目指して行きたいと思っております。

C#なネタは枯渇気味なので、どのくらい続くかわからんですが。

2014年1月7日火曜日

あ、あけましておめでとうござ。。。

うわぁ。気がつけば年が明けてしまいました。4ヵ月以上放置して…。

あ、あけましておめでとうございます。今年はもう少し更新するようにしたいです(抱負)。

とはいえC#ネタは欠乏気味で、どのくらいのペースで更新できるか…(弱気)。

ともあれ、本年もよろしくお願いいたします。