グローバルに活躍する ゲスト2名を囲んで タウンホール・ミー … · • 会員同士の個別面談、勉強会 • スタートアップとの個別面談
富山合同勉強会2015 ジェネリクス談義 C#編
-
Upload
inomata-kentaro -
Category
Engineering
-
view
159 -
download
3
Transcript of 富山合同勉強会2015 ジェネリクス談義 C#編
ジェネリクス談義 C#編
富山合同勉強会 .NET & Java 2015.1.31
自己紹介
猪股 健太郎 (@matarillo)
東京の某SI会社で(Javaと).NETの仕事
プログラマー現役続行中
「Windowsも*nixも両方やればいい!」
猫、うさぎ、酒、料理、音楽
C#と Java のジェネリクスの違い
ランタイムがフルサポート
Javaは型消去(type erasure)
値型サポート(ボックス化なし)
Javaはプリミティブ型を型引数に指定できない
配列はジェネリックコレクション
Javaの配列はジェネリックではない
変性(共変・反変)を型の定義時に指定する
Javaでは型の利用時にワイルドカードで指定する
ランタイムがフルサポート
コンパイル時にできることはすべて実行時(=リフレクション)でもできる実行時にできることはおおむねコンパイル時でもできる
実行時にキャストが発生しないので、パフォーマンスが(ちょっと)良い
ランタイムがフルサポート
例: T型の型情報を取れる
class A{
public Type GetType<T>(){
return typeof(T);}
}
Javaで言うならT.class
ランタイムがフルサポート
例: T型の配列をインスタンス化できる
class A{
public T[] ToArray<T>(T x, T y, T z){
return new T[] { x, y, z };}
}
ランタイムがフルサポート
Javaではできることができない例: ワイルドカード
class A<T> { }class B{
void M(){
A<string> a1 = new A<string>();A<Exception> a2 = new A<Exception>();List<object> list = new List<object>();list.Add(a1);list.Add(a2);
}}
JavaならA<?>と書ける
ランタイムがフルサポート
誤解の例:「Javaは型消去だからこれが不可能」class Utility{
public static T Create<T>(Dictionary<string, object> map)where T : new()
{T obj = new T();Type objType = typeof(T);foreach(PropertyInfo prop in objType.GetProperties()){
Type propType = prop.PropertyType;// propTypeをインスタンス化してobjのプロパティに設定
}return obj;
}}
値型サポート(ボックス化なし)
ボックス化が発生しないのでパフォーマンスが(ちょっと)良い
class A{
void M(){
IList<int> list = new MyArrayList<int>();list.Add(123);int head = list[0];
}}class MyArrayList<T> : IList<T>{
T[] array;}
intが使える
実行時はint配列になる
配列はジェネリックコレクション
配列を特別扱いすることが(すこし)減る
配列にもLINQ(≒Java8のStream API)が使える
int[] array = new int[] { 0, 1, 2, 3 };ICollection<int> col = array;
int[] array = new int[] { 0, 1, 2, 3 };string str = array
.Where(x => x % 2 == 0)
.Skip(1)
.Select(x => x.ToString())
.First();
変性を型の定義時に指定する
変性(共変、反変、不変)
JavaもC#も、配列は共変
※なお、Javaには「共変戻り値」というものもある
string[] array1 = new string[] { "a", "b" };object[] array2 = array1;object first = array2[0];
stringの配列型はobjectの配列型と互換性がある
(取り出す時にアップキャストしても安全)
変性を型の定義時に指定する
C#の読み取り専用コレクションは共変
IReadOnlyList<string> list1 =new List<string> { "a", "b" }.AsReadOnly();
IReadOnlyList<object> list2 = list1;object first = list2[0];
IReadOnlyList<string>型はIReadOnlyList<object>型と
互換性がある(取り出す時にアップキャストしても安全)
public interface IReadOnlyList<out T>
変性を型の定義時に指定する
C#のソート用順序比較クラスは反変
IComparer<object> cp1 = Comparer<object>.Default;IComparer<string> cp2 = cp1;Console.WriteLine(cp2.Compare("ac", "ab"));
IComparer<object>型はIComparer<string>型と
互換性がある(受け取る時にアップキャストしても安全)
public interface IComparer<in T>
変性を型の定義時に指定する
Javaの変性は型の利用時(変数の定義時)に指定
// B extends A とするpublic static void Foo(ArrayList<? extends A> list) {
A item = list.get(0);//list.add(new A());
}
public static void Bar(ArrayList<? super B> list) {//B item = list.get(0);list.add(new B());
} ArrayList<A>型は受け取り専用ArrayList<? super B>型と互換性がある
(受け取る時にアップキャストしても安全)
ArrayList<B>型は読み取り専用ArrayList<? extends A>型と互換性がある
(取り出す時にアップキャストしても安全)
C#と Java のジェネリクスの共通点
型制約
ある型Tの実体に対して何らかの処理を適用したい
何らかの処理を「安全」に適用するにはその型がどういうものか、前提知識が必要
型の前提知識=型制約
C#もJavaも、型Tに処理Mを適用できるかどうか判断する方法は、型階層のみ(型Aが型Bから派生しているかどうかを制約とする)
まとめ
細かな違いはあるが、型システムを俯瞰すると、実はそんなに違わない
C# の戦略(だと私が想像するもの) Windowsネイティブ(C/C++)との相互運用性重視⇒値型の重視、パフォーマンス重視
他の.NET言語(VB.NET)との相互運用性重視⇒ランタイムとの同調、ランタイムがサポートしない機能を含めない
ランタイムとフレームワークとの一貫性⇒ランタイムを更新するときはF/Wも更新される?