2010年10月07日

指定した複数の値を繰り返す

   このエントリーをはてなブックマークに追加 Clip to Evernote
前回の記事の続きです。

前回示した false true を繰り返す以下のコードは、bool型しか扱えないので、使い道が限られています。
任意の型にも対応させれば、使い道がありそうです。
ジェネリックスの出番ですね。
というクラスを作れば、
とか、
とか、いろいろ使えます。

foreach も使いたいということであれば、以下のようにGetEnumerable メソッドを追加します。
そうすれば、
なんて、書けますね。

ここまで書くと、別に要素が2つに限定することもないなーということで、
シーケンスを受け取る拡張メソッドを書いてみました。
これを使うコード例を以下に示します。
これだと、foreachでしか使えないんじゃないのと思う人もいると思いますが、いえいえ、次のような書き方もできます。
でも、使い勝手は良くないです。ラムダ式を返す部分をメソッドにしてしまったほうが良いかもしれません。
興味のある方は、EnumerableExtentions クラスにメソッドを追加してみてください。   

Posted by gushwell at 22:58Comments(2)TrackBack(0)

2008年10月15日

CA1002 ジェネリック リストを公開しません

   このエントリーをはてなブックマークに追加 Clip to Evernote
「CA1002 ジェネリック リストを公開しません」
これは、Visual Studio 2008のC#の静的コード分析が出すメッセージですが、
なぜ、ダメなのかいまいち理解できていませんでした。
でも、このサイトを見て
FAQ: Why does DoNotExposeGenericLists recommend that I expose Collection instead of List<T>? [David Kean]
その理由がおぼろげながら分かりました。
なるほど、ソースコードが進化(変化)する時の問題なのか。
英語が大の苦手なので、文章は良く理解できていませんが、ソースコードから
言わんとしていることが分かりました。

List<T>から派生させるな、というのもこれと関連していたんですね。


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

2008年08月06日

IEnumerator<T>は、IDisposableを実装してる(2)

   このエントリーをはてなブックマークに追加 Clip to Evernote
NyaRuRuさんの記事「IEnumerator を実装していれば必ず IDisposable である理由」を読んだ僕なりの理解。

IEnumeratorの骨格はとこんな感じです。(かなりいいかげん)

    class EnumeratorSample<T> : IEnumerator<T> {
public int Current {
get { ... }
}

public bool MoveNext() {
...
}

public void Reset() {
...
}
}

# 僕はこのクラスを実装したことは無いですが...

もし、このクラス内で解放すべきリソースを保持しているとすると、正常系では、MoveNext内で最後まで列挙が終わったことを確認した時にリソースを解放する必要があるわけですね。
でも、それだけだと完全ではないです。
途中で列挙を終えてしまった場合は、リソースを解放するタイミングがありません。
これでは困ります。
そのため、IEnumerator は、IDisposableを実装することとして、foreachで途中でbreakされた場合などでも、Disposeメソッドが呼ばれる実装にしているということだと思います。

そういう意味では、非ジェネリックなIEnumeratorでも、同じことなので、IDisposableが実装されていれば、Disposeを呼ぶようにしないといけませんね。  
Posted by gushwell at 21:26Comments(4)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年06月18日

ジェネリックメソッドの型推論

   このエントリーをはてなブックマークに追加 Clip to Evernote
最近、ようやく(今頃になって?)、C#2.0が体に沁みこんできたせいか、
ジェネリックスを使うことが多くなってきたのだが、
次のようなコードで嵌った。
 static void Main(string[] args) {
Foo(10);
int[] array = new int[] { 1,2,3 };
Foo(array);
IEnumerable<int> nums = new int[] { 1, 2, 3 };
Foo(nums);
}

static void Foo<T>(T obj) {
Console.WriteLine("T obj");
}

static void Foo<T>(IEnumerable<T> objs) {
Console.WriteLine("IEnumerable<T> objs");
}


この結果だが、

T obj
IEnumerable objs
IEnumerable objs

と表示されるのかと思ったら、

T obj
T obj
IEnumerable objs

だった。
前者だったら良かったのに。  
Posted by gushwell at 22:58Comments(2)TrackBack(0)

2008年06月05日

ジェネリックで加算は無理?

   このエントリーをはてなブックマークに追加 Clip to Evernote
T Hoge<T>(T obj) where T : struct {
return obj + 1; // + 演算子は使えない
}

のようにC#で書きたいんだけど... できません。

それで、いろいろ試してみた。
IConvertibleインターフェースには、GetTypeCode() というメソッドがあって、これは、TypeCode列挙型を返してくれる。
これを利用すれば、できるかなって思って、

T Hoge<T>(T obj) where T : struct, IConvertible {
switch (obj.GetTypeCode()) {
case TypeCode.Int32:
Int32 n = obj.ToInt32(null);
return n;
case TypeCode.Int16:
Int16 n = obj.ToInt16(null);
return n;
case TypeCode.Int64:
Int64 n = obj.ToInt16(null);
return n;
}
}

なんてコードを書いてみた。
でも、悲しいことに(というかあたり前なんだけど)、求めた n を T型にキャストすることはできないから、コンパイルエラーになる。。

やはり、無理なのかな?

素直に、
int Hoge(int obj) {
return obj + 1;
}

long Hoge(long obj) {
return obj + 1;
}


って書けばいいんじゃないの?
はい、おっしゃる通りです。

なんで、こんなことをやろうとしたかと言えば、RangeValue<T> という範囲を扱うジェネリッククラスがあって、これに IEnumerable<T> インターフェースを付加したかったから。
その際、整数の場合はデフォルトで 1 ずつ加算した値を順に取り出せるような実装を考えていんだけど、無理みたいです。

結局、

public IEnumerable<T> ToEnumerable(Func<T, T> addFunction) {
...
}

というメソッドを作り、加算部分は、ラムダ式で指定してもらうことにした。
  
Posted by gushwell at 22:19Comments(2)TrackBack(0)

2008年06月04日

ジェネリックの型引数の値はnullと比較できる

   このエントリーをはてなブックマークに追加 Clip to Evernote
private void Hoge<T>(T param) {
if ( param != null )
Console.WriteLine("is null");
}


というコードの場合、型引数Tは、値型の場合もあるし、参照型の場合もある。
通常、値型の変数と null を比較することはできないのでコンパイルエラーとなるけど、ジェネリックスの場合は、コンパイルエラーとはならない。
値型の場合は、( param != null )は、無条件に trueになる。そのため、値型の場合は、JITコンパイラーが、最適化してくれて、

    if ( param != null )
Console.WriteLine("is null");


そのもののコードを生成しないようになっているらしい。
なるほどね。  
Posted by gushwell at 21:41Comments(2)TrackBack(0)

2008年05月22日

ジェネリックなSingleton

   このエントリーをはてなブックマークに追加 Clip to Evernote
こんなクラスを書いてみた。

public sealed class Singleton<T> where T : class {
public static T Instance {
get { return singleton; }
}
static Singleton() {
Type t = typeof(T);
var obj = Activator.CreateInstance(t, true);
singleton = obj as T;
}
private static readonly T singleton = null;
}


このクラスを使えば、

Sample o = Singleton<Sample>.Instance;


なんていう書き方ができる。
そして、Sampleクラスのコンストラクタを privateにすれば、

Sample o = new Sample();


という書き方もできなくなる。
リフレクションを使っているはそのためだ。  
Posted by gushwell at 21:27Comments(4)TrackBack(0)