あらゐけいいちがモーニングでもあらゐけいいちでうれしい。楽しみが増えた。
2016年10月1日土曜日
2016年8月1日月曜日
IDisposable 汎用クラスの実装
ものすごく抽象的になりますが、おおよそこんなコードがありました。
ところが実際には、A.DoにしろB.Doにしろ、省略しているそれ以外のところでも、例外がスローされる可能性がありました。コンストラクト済みのA、Bの両オブジェクトは、Cleanupメソッドを呼ぶ必要があり、例外がスローされた場合も正しく処理されるためには、以下のようにコードを修正する必要がありました。
結果として、A、Bいずれも宣言と同時に初期化ができなくなり、finally句内の後片付けも、構築済みかどうかのチェックが必要になってしまいました。
実際、この手のコードはたまに見かけます。正しく書くとこうなってしまうケース。でも、正直カッコワルいよね。ちなみに、このケースならA,Bの両クラスがIDisposableを実装して、DisposeでCleanupできれば問題ないんだけど、そうもいかないケースも結構多いですよね。
で、考えてみたのが「それなら、IDisposableの汎用クラスを作ってしまえば良いんじゃないか」ということ。試しに作ってみたのは、こんなどシンプルなクラス。クラス名は「Disposeするヒト」なので、「Disposer」クラス。
これを作っておくことで、先のコードはこう変わります。
usingのスコープを抜けたときに行う処理を、あらかじめ定義しておくイメージでしょうか。ちなみに、using句でnewされたオブジェクトが特に参照されないのであれば、変数に代入しなくてもちゃんと動くんですね。初めて知った。
var a = new A(); a.Do(); var b = new B(a); b.Do(); // 後片付け b.Cleanup(); a.Cleanup();
ところが実際には、A.DoにしろB.Doにしろ、省略しているそれ以外のところでも、例外がスローされる可能性がありました。コンストラクト済みのA、Bの両オブジェクトは、Cleanupメソッドを呼ぶ必要があり、例外がスローされた場合も正しく処理されるためには、以下のようにコードを修正する必要がありました。
A a = null;
B b = null;
try
{
a = new A();
a.Do();
b = new B(a);
b.Do();
}
finally
{
if (b != null)
b.Cleanup();
if (a != null)
a.Cleanup();
}
結果として、A、Bいずれも宣言と同時に初期化ができなくなり、finally句内の後片付けも、構築済みかどうかのチェックが必要になってしまいました。
実際、この手のコードはたまに見かけます。正しく書くとこうなってしまうケース。でも、正直カッコワルいよね。ちなみに、このケースならA,Bの両クラスがIDisposableを実装して、DisposeでCleanupできれば問題ないんだけど、そうもいかないケースも結構多いですよね。
で、考えてみたのが「それなら、IDisposableの汎用クラスを作ってしまえば良いんじゃないか」ということ。試しに作ってみたのは、こんなどシンプルなクラス。クラス名は「Disposeするヒト」なので、「Disposer」クラス。
public class Disposer : IDisposable
{
private readonly Action disposer;
public Disposer(Action disposer)
{
this.disposer = disposer;
}
public void Dispose()
{
disposer();
}
}
これを作っておくことで、先のコードはこう変わります。
var a = new A();
using (new Disposer(() => a.Cleanup()))
{
a.Do();
var b = new B(a);
using (new Disposer(() => b.Cleanup()))
{
b.Do();
}
}
usingのスコープを抜けたときに行う処理を、あらかじめ定義しておくイメージでしょうか。ちなみに、using句でnewされたオブジェクトが特に参照されないのであれば、変数に代入しなくてもちゃんと動くんですね。初めて知った。
ラベル:
C#
もう8月ですね...
ほとんど更新しないまんまもう8月。月日の経つのはほんと早い。
いや、真田丸も面白いし、相変わらず漫画もたくさん読んでるし、ポケモンGOも始めたしで、書くことないわけでもないんですが、やっぱり本筋のC#/LINQ的なネタがないと、他のことも書こうという気になかなかならず...。
ちょっとだけネタができたので、ぼちぼち行きます。
いや、真田丸も面白いし、相変わらず漫画もたくさん読んでるし、ポケモンGOも始めたしで、書くことないわけでもないんですが、やっぱり本筋のC#/LINQ的なネタがないと、他のことも書こうという気になかなかならず...。
ちょっとだけネタができたので、ぼちぼち行きます。
2016年1月20日水曜日
2016年1月18日月曜日
「真田丸」が面白い!
おぉ。いつの間にか年が明けておりますね…。ごにょごにょ…。
…さて、今年の大河ドラマ「真田丸」が面白いです!すごく!
大河ドラマはどうしても、最初のほうは、時代の背景や主人公の特に幼少期のエピソードなどの説明が主になってしまい、最初の二か月ぐらいは我慢してみてると、そのうち面白くなってくる。というのがパターンでした。
が、今回はもう最初から面白い。
予想していたのは、長篠の戦いあたりがスタートで、信幸・信繁兄弟の幼少期から入ると思っていました。が、幼少期はぶっとばして、いつも通りの子役からなんてことはせず、武田家滅亡の直前からのスタートで、堺さんと大泉さんに14~5歳をやらせた。たぶん、正解だと思う。
その堺さんと大泉さん。始まる前は「逆じゃね?」と思ったけど、すごくしっくりくる。そんでなにより、おとーちゃんの草刈正雄。もーぴったりすぎ。(昔のNHKの「真田太平記」で幸村(信繁)役だったらしい。)
お話もすばらしい。わかりやすい。展開早くてだれない。そして大河なのにところどころに笑いを差し込んでくる。いーねー!
そして、過去の大河ドラマでよく不満に思っていたのは、戦闘などがあった時にその地理的な位置関係などの説明が少なく、なんでその結果になったのか、よくわからないことが多かった。
しかし、今回は非常にわかりやすい地図と共に背景を説明してくれるので、理解が早くて話に入りやすい。この地図がいい!(オープニングのタイトルロールで、「シブサワ・コウ」の名前を見たときはビビりました。)
ひとまず二話まで終わった時点では先が楽しみで仕方がないです。
…そして、Twitterの「#真田丸どうでしょう」に爆笑。
…さて、今年の大河ドラマ「真田丸」が面白いです!すごく!
大河ドラマはどうしても、最初のほうは、時代の背景や主人公の特に幼少期のエピソードなどの説明が主になってしまい、最初の二か月ぐらいは我慢してみてると、そのうち面白くなってくる。というのがパターンでした。
が、今回はもう最初から面白い。
予想していたのは、長篠の戦いあたりがスタートで、信幸・信繁兄弟の幼少期から入ると思っていました。が、幼少期はぶっとばして、いつも通りの子役からなんてことはせず、武田家滅亡の直前からのスタートで、堺さんと大泉さんに14~5歳をやらせた。たぶん、正解だと思う。
その堺さんと大泉さん。始まる前は「逆じゃね?」と思ったけど、すごくしっくりくる。そんでなにより、おとーちゃんの草刈正雄。もーぴったりすぎ。(昔のNHKの「真田太平記」で幸村(信繁)役だったらしい。)
お話もすばらしい。わかりやすい。展開早くてだれない。そして大河なのにところどころに笑いを差し込んでくる。いーねー!
そして、過去の大河ドラマでよく不満に思っていたのは、戦闘などがあった時にその地理的な位置関係などの説明が少なく、なんでその結果になったのか、よくわからないことが多かった。
しかし、今回は非常にわかりやすい地図と共に背景を説明してくれるので、理解が早くて話に入りやすい。この地図がいい!(オープニングのタイトルロールで、「シブサワ・コウ」の名前を見たときはビビりました。)
ひとまず二話まで終わった時点では先が楽しみで仕方がないです。
…そして、Twitterの「#真田丸どうでしょう」に爆笑。
2015年11月5日木曜日
2015年10月22日木曜日
SJISのCSVファイルを各カラムの操作と条件による絞り込みを行い、UTF-8のCSVファイルを出力する処理をLINQで。(改)
もともとは「ラムダ式を利用したリファクタリングの例 その2」で扱った後、さらに「SJISのCSVファイルを各カラムの操作と条件による絞り込みを行い、UTF-8のCSVファイルを出力する処理をLINQで。」で修正版を示したネタです。改二ですな。
こんな要求に対する処理を書いていました。
という処理になります。日本語とほぼ一対一にコードが対応してます。ちなみに、.net 2.0相当でコードを書くとこんな感じ。
こんな要求に対する処理を書いていました。
- Shift-JISのCSVファイルを入力し、UTF-8のCSVファイルを出力する。
- 入力したCSVの各カラムは、固定長で前後に空白が入る可能性があり、その空白は除去して出力する。
- 各行の先頭のカラムはIDになっていて、特定のIDのみ出力対象とする。
inFileが入力ファイルのパス、outFileが出力ファイルのパスとして、絞り込みの条件を「先頭カラムが奇数」とすると、以下のコードで拡張メソッドとか作らなくても要求が満たせてしまいますね。
File.WriteAllLines(
outFile,
File.ReadLines(inFile, Encoding.GetEncoding("shift-jis"))
.Select(line => line.Split(','))
.Where(items => int.Parse(items[0]) % 2 != 0)
.Select(items => string.Join(",", items.Select(item => item.Trim()))));
- inFileからShift-JISで各行を読み込み、
- ',' で分割し、
- 先頭カラムが奇数の行について、
- 各カラムの前後の空白を除去した上で再度 ',' で結合し、
- outFileに書き込む。
という処理になります。日本語とほぼ一対一にコードが対応してます。ちなみに、.net 2.0相当でコードを書くとこんな感じ。
var sb = new StringBuilder();
string line;
using (var sr = new StreamReader(inFile, Encoding.GetEncoding("shift-jis")))
using (var sw = new StreamWriter(outFile))
{
while ((line = sr.ReadLine()) != null)
{
var items = line.Split(',');
sb.Length = 0;
if (int.Parse(items[0]) % 2 != 0)
{
foreach (var item in items)
{
sb.Append(item.Trim()).Append(',');
}
sw.WriteLine(sb.ToString(0, sb.Length - 1));
}
}
}
雲泥の差がありますねぇ…。
ラベル:
C#
登録:
投稿 (Atom)