第20回codeseek勉強会

第20回codeseek勉強会を開催します。

今回は、TechEdの開発者で一番話題になったLINQを取り上げます。

LINQは、データアクセスをこれまでとは違った視点で表現しようとする

革新的テクノロジーですが、必然的に、いままでのコーディング技術、

管理技術が通用しなくなるものでもあります。

今回の勉強会では、このLINQがアプリケーションプログラミングに

おいて、コーディングに及ぼす影響を中心に意見交換をする場に

したいと思っています。

第20回codeseek勉強会

「LINQとアプリケーションプログラムコード」

(共催:tk-engineering、こみゅぷらす、eパウダ~)

2007年9月29日(土)

10:00~12:00

場所:マイクロソフト新宿オフィス

募集締め切り:9月25日(火)23時59分99

codeseek 勉強会のお知らせ

第20回codeseek勉強会が開催されます。

内容に興味のある方は申し込みしてみてはいかがでしょうか。

申し込みはこちらから。

Share

CommandBuilderのConflictOptionについて

ConflictOptionをCompareRowVersionに指定しているとき、

テーブルにtimestamp(rowversion)列が存在するときは、主キー+timestamp列で、

テーブルにtimestamp(rowversion)列が存在しないときは、主キーのみ(overwriteChanges相当)で

where句が生成される。

timestamp列が存在しないときは、

CompareAllSearchableValues(全ての項目を比較する。)相当になったほうが安全なのになとふと思った。

まぁ、全テーブルにtimestamp列があれば、何も困る事は無いんだけれども。

Share
カテゴリー: .NET

読みやすいコードのためにLINQを利用する。

最初に断っておきたいこと

C#3.0の話です。
LINQやラムダ式自体の説明はしていません。
簡単なサンプルにするため仕様がシンプルで効果を感じられないかもしれませんが、
もしもっと複雑な仕様だったら等、想像してみてください。

まず最初に、下記のサンプルを見てください。

簡単な仕様として、

受け取った在庫リストの集計対象のものだけ集計したい。
集計対象とは、数量が10より大きくて、数量を1000で割った時の余りが0のものの事である。

を実現します。

class サンプル1
{
private class 在庫計
{
public int 数量計 { get; set; }
public int 金額計 { get; set; }
}
public void サンプルメソッド(在庫[] 在庫リスト)
{
在庫計 在庫計情報 = new 在庫計();
foreach (在庫 在庫情報 in 在庫リスト)
{
if (在庫情報.数量 > 10)
{
if ((在庫情報.数量 % 1000 == 0))
{
在庫計情報.数量計 += 在庫情報.数量;
在庫計情報.金額計 += 在庫情報.金額;
}
}
}
}
}

この例の場合、たしかに仕様を実現していますが、if文が実現している仕様についてコード上から読み取ることができません。

そこで、ラムダ式を利用し、集計対象の時にtrueを返す匿名メソッドを作成して実装してみます。

class サンプル2
{
private class 在庫計
{
public int 数量計 { get; set; }
public int 金額計 { get; set; }
}
public void サンプルメソッド(在庫[] 在庫リスト)
{
Func<在庫, Boolean> 集計対象 = 在庫情報 =>
{
if (在庫情報.数量 <= 10)
{
return false;
}
if (在庫情報.数量 % 1000 != 0)
{
return false;
}
return true;
};
在庫計 在庫計情報 = new 在庫計();
foreach (在庫 在庫情報 in 在庫リスト)
{
if (集計対象(在庫情報))
{
在庫計情報.数量計 += 在庫情報.数量;
在庫計情報.金額計 += 在庫情報.金額;
}
}
}

その結果、集計対象の在庫のみ集計するという部分と、集計対象である条件の詳細部分が分離されました。

ここでさらにLINQを利用して、ループの部分が実際に何をしているのかをコードに残すようにします。

public class サンプル3
{
public void サンプルメソッド(在庫[] 在庫リスト)
{
Func<在庫, bool> 集計対象 = 在庫情報 =>
{
if (在庫情報.数量 <= 10)
{
return false;
}
if (在庫情報.数量 % 1000 != 0)
{
return false;
}
return true;
};
var 在庫計情報 = new
{
数量計 = 在庫リスト
.Where(集計対象)
.Sum(x => x.数量),
金額計 = 在庫リスト
.Where(集計対象)
.Sum(x => x.金額)
};
}
}

もしくは、

public class サンプル4
{
public void サンプルメソッド(在庫[] 在庫リスト)
{
Func<在庫, bool> 抽出条件 = 在庫情報 =>
{
if (在庫情報.数量 <= 10)
{
return false;
}
if (在庫情報.数量 % 1000 != 0)
{
return false;
}
return true;
};
var 在庫計情報 = new
{
数量計 = (from 在庫情報 in 在庫リスト
where 抽出条件(在庫情報)
select 在庫情報.数量).Sum()
,
金額計 = (from 在庫情報 in 在庫リスト
where 抽出条件(在庫情報)
select 在庫情報.金額).Sum()
};
}
}

その結果、ループがなくなりましたが、在庫リストから集計対象のもののみを抽出(Where)し、集計(Sum)している事がコードに埋め込まれました。

また、在庫計情報をループで加算しないため、サンプル2までは在庫計というinner classを作成していましたが、その必要がなくなり、その場限りの匿名型で済むようになりました。

ところでサンプルコードだけを見ているとわかりませんが、集計した結果がint型の範囲を超えた時、サンプル2まで(ループで集計している)とサンプル3以降(LINQを利用している。)で実行時の挙動が違います。

サンプル2までは、例外が発生せず、マイナス値になってしまいます。

サンプル3以降は、ちゃんとOverflowExceptionが発生します。

もしサンプル2までのコードで例外が発生するようにする場合は

単純に

//抜粋
foreach (在庫 在庫情報 in 在庫リスト)
{
if (集計対象(在庫情報))
{
if (在庫計情報.金額計 + 在庫情報.金額 > int.MaxValue)
{
throw new OverflowException();
}
在庫計情報.数量計 += 在庫情報.数量;
在庫計情報.金額計 += 在庫情報.金額;
}
}

とすると、intの範囲を超えた場合

在庫計情報.金額計 + 在庫情報.金額

がマイナス値になるため

在庫計情報.金額計 + 在庫情報.金額 > int.MaxValue

の条件がtrueになることが無いため、いつまでも例外が発生しません。

そのためより値のとれる範囲の大きいlongに型変換したうえで、

//抜粋
foreach (在庫 在庫情報 in 在庫リスト)
{
if (集計対象(在庫情報))
{
if ((long)在庫計情報.金額計 + (long)在庫情報.金額 > int.MaxValue)
{
throw new OverflowException();
}
在庫計情報.数量計 += 在庫情報.数量;
在庫計情報.金額計 += 在庫情報.金額;
}
}

とするか、金額にマイナス値が無い前提であれば、

//抜粋
foreach (在庫 在庫情報 in 在庫リスト)
{
if (集計対象(在庫情報))
{
if (在庫計情報.金額計 + 在庫情報.金額 < 0)
{
throw new OverflowException();
}
在庫計情報.数量計 += 在庫情報.数量;
在庫計情報.金額計 += 在庫情報.金額;
}
}

とする必要があります。

つまり、うっかり間違った実装をしてしまうと、オーバフロー時に例外を発行させているつもりで、実は発行していないという状況になってしまいますので、そういう点でも、このくらいの集計ならLINQを利用した方が安全です。

Share

「自分は正しい」という顔をして、ただけなすだけ

ユーザーが選択した手法を検討せずに、プロジェクトを進めていつも失敗してるんだろうな

全Windowsの評価版の入手方法 – System Insider

この発言をする必要性は全くないのではないだろうか。

スレッドの話題からは完全にずれて、ただ想像で相手をけなしているだけの発言だろう。

この前で止めておけばよかったのではないだろうか。

蛇足という奴だ。

「間違い投稿や誤解をまねくような投稿」に対して、訂正・謝罪があればありがたいとは思うけど、無かったからと言って上記のような投稿が、「間違い投稿や誤解をまねくような投稿」に対して何ら有益ではないし、だったら訂正・補足の投稿をしてはいかがかと思う。

まぁ、価値観が多様化している事もあるから、私の感覚がずれているだけなのかも。

とりあえず、私は同じ事をしないように気をつけるとしよう。

Share

try-finallyをはき出すスニペットについて。

try-catchを吐き出すスニペットが[try]であることに対して、

try-finallyを吐き出すスニペットは[tryf]である。

これが、

try-catchを吐き出すスニペットが[tryc]であることに対して、

try-finallyを吐き出すスニペットは[try]である。

だったら、もう少し無駄なcatchが減るのかもしれないなぁ。

Share
カテゴリー: .NET

読みやすいコードを書くために匿名型を利用する。

最初に断っておきたいこと

VB9の話です。
匿名型自体の説明はしていません。
最後になぜ今回はC#ではなくVBなのか説明します。
簡単なサンプルにするため仕様がシンプルで効果を感じられないかもしれませんが、
もしもっと複雑な仕様だったら等、想像してみてください。

まず最初に先日の匿名メソッドのエントリでも私が考える読みやすいコードの要素について書きましたが、今回は

1.処理のスコープを最小にする。

2.よりコードに意味を持たせる。

という2点について注目していきたいと思います。

まず最初に、下記のサンプルを見てください。

簡単な仕様として、引数で渡された在庫リストの数量と金額の合計を算出します。

Public Class 在庫
Public 数量 As Integer
Public 金額 As Integer
 ''以下定義が続く
End Class
Public Class サンプル1
Public Sub サンプルメソッド(ByVal 在庫リスト As 在庫())
Dim 数量計 As Integer = 0
Dim 金額計 As Integer = 0
For Each 在庫情報 As 在庫 In 在庫リスト
数量計 = 在庫情報.数量
金額計 = 在庫情報.金額
Next
End Sub
End Class

この例の場合、確かに、数量計と金額計を算出したいのはわかりますが、数量計と金額計はそれぞれ独立した変数となっているため、関連があることをコード上に残せていません。

そこで次のサンプルを見てください。

Public Class サンプル2
Private Class 在庫計
Public 数量計 As Integer = 0
Public 金額計 As Integer = 0
End Class
Public Sub サンプルメソッド(ByVal 在庫リスト As 在庫())
Dim 在庫計情報 As New 在庫計
For Each 在庫情報 As 在庫 In 在庫リスト
在庫計情報.数量計 = 在庫情報.数量
在庫計情報.金額計 = 在庫情報.金額
Next
End Sub
End Class

privateなinner classである在庫計を作成し、サンプルメソッド内で、インスタンスを生成し利用することで、数量計と金額計は独立した変数ではないという事と、在庫の合計を保持しているという意味を持たせる事ができました。

その反面、在庫計クラスはサンプル2クラス全体から利用できるようになってしまいましたので、ほかのメソッドでも使っているかもしれない可能性が生まれてしまいました。

ここで、次のサンプルを見てください。

Public Class サンプル3
Public Sub サンプルメソッド(ByVal 在庫リスト As 在庫())
Dim 在庫計情報 = New With {.数量計 = 0, .金額計 = 0}
For Each 在庫情報 As 在庫 In 在庫リスト
在庫計情報.数量計 = 在庫情報.数量
在庫計情報.金額計 = 在庫情報.金額
Next
End Sub
End Class

数量計と金額計をメンバに持った匿名型としてインスタンスを生成し、変数に適切な名前を設定することで、サンプル2の利点を生かしたまま、在庫計というクラスを作成しているわけではないので、他のメソッドでも利用されているかもしれないといった可能性が発生しなくなりました。

このように、匿名型を利用することで、できるだけ少ない範囲のスコープを維持したまま、よりコードに意味を持たせる事ができます。

最後に、何故C#ではなくVBで書いたのかというと、

C#の匿名型はメンバがすべて読み取り専用のプロパティとして宣言されるため、最初に宣言した時に設定した値を変更する事ができないからです。

VBでいうところの

Public Class サンプル4
Public Sub サンプルメソッド(ByVal 在庫リスト As 在庫())
Dim 在庫計情報 = New With {key .数量計 = 0,key  .金額計 = 0}
For Each 在庫情報 As 在庫 In 在庫リスト
在庫計情報.数量計 = 在庫情報.数量
在庫計情報.金額計 = 在庫情報.金額
Next
End Sub
End Class

というコードになってしまい、コンパイルエラーになるという事ですね。

では、最初の設定時に集計した値を設定してやれば良いのですが、その話は次回以降のエントリにします。

Share

仕事が出来ない人の7つの習慣

ベストセラーの「7つの習慣」をもじった反面教師的記事をご紹介。「7 Habits of Highly Ineffective People」なる主張です。

たしかにこういう部下や同僚がいたらイラっと来てしまうかも・・・と思う前にまずは自分がひっかかっていないかチェックですね・・・。

仕事が出来ない人の7つの習慣 | P O P * P O P

思い当たる節があったりして、心が痛いです。

Share

ラムダ式の=>演算子の読み方。(訂正しました)

ラムダ式の=>演算子(らむだえんざんし)をどう発音するのかわからなかったのですが、ちゃんとリファレンスに載ってました。

「goes to」と読むんですね。

ということで、

x => x + 1;

x goes to x plus one.

と読むんだそうで、どうでもいいことですが・・・。

#訂正しました。

どこでどう間違ったのか・・・。

goes on ではなく goes toが正解です。

申し訳ありません。

Share

読みやすいコードを書くためにFunc型と匿名メソッドを利用する。

最初に断っておきたいこと

C#3.0の話です。
Func型や匿名メソッド自体の説明はしていません。

まず最初に、私が考える読みやすいコードの要素について断片的に提示します。

1.処理のスコープを最小にする。

2.今、読みたい粒度のコードしか読まなくても良い。

というものがあります。

どれも、いくつかある読みやすいコードの要素の断片でしかありませんが、この1と2は矛盾を孕んでいます。

それでは、どんな矛盾を孕んでいるかサンプルを交えて説明します。

001     public class サンプル1
002     {
003         public void サンプルメソッド()
004         {
005             if (鼻毛抜きの在庫 == 0 && 鼻毛カッターの在庫 == 0)
006             {
007                 return;
008             }
009 
010             鼻毛処理();
011         }
012     }
013 

鼻毛処理のできる道具があれば、鼻毛処理ができる。

という仕様を実現しています。

しかしながら、「鼻毛処理のできる道具があれば」の詳細部分まで同じメソッド内に記述されてしまっています。

そこで、

001     public class サンプル2
002     {
003         public void サンプルメソッド()
004         {
005             if (!鼻毛処理の道具がある())
006             {
007                 return;
008             }
009 
010             鼻毛処理();
011         }
012 
013         private bool 鼻毛処理の道具がある()
014         {
015             if (鼻毛抜きの在庫 > 0)
016             {
017                 return true;
018             }
019 
020             if (鼻毛カッターの在庫 > 0)
021             {
022                 return true;
023             }
024 
025             return false;
026         }
027 
028         public void その他の処理()
029         {
030             throw new NotImplementedException();
031         }
032 
033     }
034 

これで、「2.今、読みたい粒度のコードしか読まなくても良い。」という要素が満たされた実装になりました。

しかしメソッドアウトする前は、処理のスコープがメソッド内にしかなかったのに、メソッドアウトした事で処理のスコープがクラス全体に広がってしまいました。

実装したばかりの今なら、まだサンプルメソッド内でしか使われていないとわかりますが、将来このコードを見たときに、サンプルメソッド内でしか使われていないと断定することができません。

これでは、「1.処理のスコープを最小にする」という要素が満たされなくなってしまっています。

そこで、以下のようにFunc型と匿名メソッドを利用してサンプルを修正してみます。

001     public class サンプル3
002     {
003         public void サンプルメソッド()
004         {
005             Func<bool> 鼻毛処理の道具がある =
006                 delegate
007                 {
008                     if (鼻毛抜きの在庫 > 0)
009                     {
010                         return true;
011                     }
012 
013                     if (鼻毛カッターの在庫 > 0)
014                     {
015                         return true;
016                     }
017 
018                     return false;
019                 };
020 
021             if (!鼻毛処理の道具がある())
022             {
023                 return ;
024             }
025 
026             鼻毛処理();
027         }
028 

これで、匿名メソッドという形でメソッドアウトし「2.今、読みたい粒度のコードしか読まなくても良い。」という要素を満たした上に、処理のスコープもメソッド内にとどまりますので、「1.処理のスコープを最小にする。」という要素も満たされます。

「鼻毛処理の道具があれば」という仕様の詳細まで知る必要が無いときは匿名メソッドを読み飛ばせば良いのです。

以上のように「ひとつのメソッドの中でしか利用しないが、粒度が違うためメソッドアウトする。」といったような局面において最小のスコープを維持したままメソッドアウトする道具として匿名メソッドを使う例の紹介でした。

ちなみに、サンプル3をラムダ式で書くと

001     public class サンプル3
002     {
003         public void サンプルメソッド()
004         {
005             Func<bool> 鼻毛処理の道具がある =
006                 () =>
007                 {
008                     if (鼻毛抜きの在庫 > 0)
009                     {
010                         return true;
011                     }
012 
013                     if (鼻毛カッターの在庫 > 0)
014                     {
015                         return true;
016                     }
017 
018                     return false;
019                 };
020 
021             if (!鼻毛処理の道具がある())
022             {
023                 return ;
024             }
025 
026             鼻毛処理();
027         }
028 

となります。

Share