2008年07月31日

Cast<>演算子

   このエントリーをはてなブックマークに追加 Clip to Evernote
WindowsFormsアプリのフォームに配置されたコントロールの中から、Butonnだけを取り出しすコードを C#+LINQで書こうとしたら、上手くいきません。

var buttons = this.Controls.Where(ctrl => ctrl is Button);

と書くとエラーになります。

Controlsプロパティは、ControlCollection クラスで、このクラスは、IEnumerable<T>を実装していないためにエラーになってしまうようです。

そんな時にCast演算子が使えます。

var buttons = this.Controls.Cast<Control>().Where(ctrl => ctrl is Button);

って書けばOKでした。  

Posted by gushwell at 21:18Comments(7)TrackBack(0)

2008年07月29日

拡張メソッドと単項演算子の優先順位

   このエントリーをはてなブックマークに追加 Clip to Evernote
static class DecimalExtentions {
public static decimal Floor(this decimal num) {
return decimal.Floor(num);
}
}

という拡張メソッドをC#で書く。
これで、

Decimal.Floor(124.4m)

ではなく、

124.4m.Floor();

って書けるようになる。

でテストコードを書いてみたら、

Assert.AreEqual(-125m, -124.5m.Floor());

が、失敗してしまう。

Console.WriteLine(-124.5m.Floor());

の結果は、

-124だ。

あれ、変だな??? -125になってないぞ。

試しに

Assert.AreEqual(-125m, Math.Floor(-124.5m));

て書いたらOKだ。

一瞬、decimal.Floorのバグ?って思ったけど、ステップ実行してみたわかった。

    public static decimal Floor(this decimal num) {
return decimal.Floor(num);
}

の引数には、-124.5m じゃなく、124.5mが渡って来てる。
そうか、結合の優先順位が、メソッド呼び出しの方が高いんだ。
メソッド呼び出しの後から、単項演算子のマイナスが評価されるから、

 -decimal.Floor(124.5m)

になっちゃうんだ。  
Posted by gushwell at 21:55Comments(0)TrackBack(0)

2008年07月22日

順列生成メソッド:僕も書いてみた

   このエントリーをはてなブックマークに追加 Clip to Evernote
元ネタ:http://blogs.wankuma.com/rti/archive/2008/07/18/149606.aspx

僕もC#3.0で作ってみた。


public IEnumerable<string> GetPermutation(string str) {
if (str.Length == 1)
yield return str;
else {
char[] array = str.ToCharArray();
for (int i = 0; i < array.Length; i++) {
Swap(array, 0, i);
foreach (var s in GetPermutation(new string(array,1, str.Length - 1)))
yield return array[0] + s;
}
}
}

private void Swap(char[] array, int i, int j) {
char temp = array[i];
array[i] = array[j];
array[j] = temp;
}


これで、

 GetPermutation("abcd").ToList().ForEach(s => Console.WriteLine(s));


と書けば、答えがプリントされる。

テストが十分じゃないけど、たぶんいけると思う。

  
Posted by gushwell at 22:47Comments(3)TrackBack(1)

2008年07月16日

ProxyAttribute

   このエントリーをはてなブックマークに追加 Clip to Evernote
ProxyAttributeについて調べようと思って、Googleで検索したら、
このブログの3年前の記事
http://blog.livedoor.jp/gushwell/archives/22297134.html
がヒットした。orz...

こんな記事を書いたのを完全に忘れていた。
ちょっとショック。


  
Posted by gushwell at 22:56Comments(0)TrackBack(0)

2008年07月09日

LINQ Except拡張メソッドの不思議

   このエントリーをはてなブックマークに追加 Clip to Evernote
LINQ には、以下のようなExcept拡張メソッドがある、

public static IEnumerable<TSource> Except<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer
)


指定された IEqualityComparer を使用して値を比較し、2つのシーケンスの差集合を取り出すメソッドだ。

この Except拡張メソッドでハマった。

以下のような比較クラスを書き、そのインスタンスを引数に与えているのに、なぜか正しく動いてくれない。

class PropComparer : IEqualityComparer<PropertyInfo> {
public bool Equals(PropertyInfo x, PropertyInfo y) {
return x.Name == y.Name;
}

public int GetHashCode(PropertyInfo obj) {
return obj.GetHashCode();
}
}

Equalsメソッドにブレークポイントを設定してみたけど、止まることなく、Exceptメソッドが終わってしまう。
そんなはずはないんだけど...
しばらく悩んだ末に、書いたのが以下の明示的にインターフェースを実装したコード。

class PropComparer : IEqualityComparer<PropertyInfo> {
public bool Equals(PropertyInfo x, PropertyInfo y) {
return x.Name == y.Name;
}

public int GetHashCode(PropertyInfo obj) {
return obj.Name.GetHashCode();
}

bool IEqualityComparer<PropertyInfo>.Equals(PropertyInfo x, PropertyInfo y) {
return Equals(x, y);
}

int IEqualityComparer<PropertyInfo>.GetHashCode(PropertyInfo obj) {
return this.GetHashCode();
}
}

わけ分からないけど、こっちはちゃんと動いた。
明示的にインターフェースを実装したEqualsが呼び出されているようだ。
そういうものなのかな? いまいちどうしてなのか分からない。

普通、明示的にインターフェースを実装しなくても、インターフェース経由での呼び出しもできるはずなんだけど... ちがったっけ?

----
2007/7/10 追記

実は、PropComparerクラスに、明示的にインターフェースを実装した際に、GetHashCodeの中身が不適切だったことに気がつき、

    public int GetHashCode(PropertyInfo obj) {
return obj.Name.GetHashCode();
}


と変更している。これで正しく動くことを確認。
明示的にインターフェースを実装していなかったバージョンでは、GetHashCodeを直してのテストはしなかった。

でも、NyaRuRuさんの指摘で、はたと気がつきました。
HashCodeが非適切な値を返していたために、Equalsメソッドが呼ばれなかったんだ!
インターフェースを明示的に実装している/していないは、まったく関係ないのに、勘違いもはなはだしいです。


class PropComparer : IEqualityComparer<PropertyInfo> {
public bool Equals(PropertyInfo x, PropertyInfo y) {
return x.Name == y.Name;
}

public int GetHashCode(PropertyInfo obj) {
return obj.Name.GetHashCode();
}
}


とGetHashCodeを変更し、動かしてみたところ、あたりまえだけど、ちゃんと Exceptメソッドが動いた。  
Posted by gushwell at 22:14Comments(2)TrackBack(0)

2008年07月08日

ArrayList, Hashtableよりはジェネリック・クラスを

   このエントリーをはてなブックマークに追加 Clip to Evernote
このブログのアクセス解析でどのキーワードで検索されたかを見てみると意外とArrayListやHashtableで検索されていたりする。

Visual Studio 2005以降を使っているのならば、是非。List<T>, Dictionary<T>を使ってほしいものだ。

ArrayList ではなく List<T> を使う
Hashtable ではなく、Dictionary<T> を使う

これらのジェネリック・クラスを使えば、安全性が高まるし、キャストも不要だ。

Dictionay<T>クラスの簡単な使用例

 Dictionary<string, double> dict = new Dictionary<string, double>();
dict.Add("Fooe", 10.8);
dict.Add("Bar", 12.2);
dict.Add("Buz", 30.4);
dict.Add("Qux", 42.0);

Console.WriteLine(dict["Buz"]);

foreach (KeyValuePair<string, double> kv in dict)
Console.WriteLine(kv.Key + " = " + kv.Value);


ちなみに C#3.0ならば

 foreach (var kv in dict)
Console.WriteLine(kv.Key + " = " + kv.Value);


って書ける。
最近、foreach文で、型名を書くのが億劫になってきた...  
Posted by gushwell at 21:29Comments(0)TrackBack(0)

2008年07月07日

C#らしく書いてほしい

   このエントリーをはてなブックマークに追加 Clip to Evernote
これ、MSDN ライブラリに掲載されていたコードの抜粋。

    FileCodeModel fileCM = dte.ActiveDocument.ProjectItem.FileCodeModel; 
CodeElements elts = null;
elts = fileCM.CodeElements;
CodeElement elt = null;
int i = 0;
MessageBox.Show( "about to walk top-level code elements ...");
for ( i=1; i<=fileCM.CodeElements.Count; i++ )
{
elt = elts.Item( i );
CollapseElt( elt, elts, i );
}


うーん、もう少し、C#らしく書いてね!と言いたい。
やっぱり、以下のように書いて欲しい。

    FileCodeModel fileCM = dte.ActiveDocument.ProjectItem.FileCodeModel; 
CodeElements elts = fileCM.CodeElements;
MessageBox.Show( "about to walk top-level code elements ...");
for ( int i=1; i<=fileCM.CodeElements.Count; i++ )
{
CodeElement elt = elts.Item( i );
CollapseElt( elt, elts, i );
}


それよりも、なんで、Itemのインデックスは 1 からなんだ?  
Posted by gushwell at 21:55Comments(0)TrackBack(0)

2008年07月04日

decimal その2 (演算後の小数部の桁数)

   このエントリーをはてなブックマークに追加 Clip to Evernote
昨日のdecimalの続き
じゃあ、これはどうだ。

decimal a = 50.00m;
decimal b = 10.0m;
decimal c = a * b;
decimal d = a + b;
decimal e = a / b;
Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(e);

結果は、

500.000
60.00
5.0

だ。
ふーん。なるほどね。割り算の結果が、

5.0

となるとは意外だな。
筆算をシミュレートしているような結果だ。
そういう意味では理屈にあっているな。
  
Posted by gushwell at 22:33Comments(0)TrackBack(0)