富山合同勉強会2015 ジェネリクス談義 C#編

16
ジェネリクス談義 C#富山合同勉強会 .NET & Java 2015.1.31

Transcript of 富山合同勉強会2015 ジェネリクス談義 C#編

Page 1: 富山合同勉強会2015 ジェネリクス談義 C#編

ジェネリクス談義 C#編

富山合同勉強会 .NET & Java 2015.1.31

Page 2: 富山合同勉強会2015 ジェネリクス談義 C#編

自己紹介

猪股 健太郎 (@matarillo)

東京の某SI会社で(Javaと).NETの仕事

プログラマー現役続行中

「Windowsも*nixも両方やればいい!」

猫、うさぎ、酒、料理、音楽

Page 3: 富山合同勉強会2015 ジェネリクス談義 C#編

C#と Java のジェネリクスの違い

ランタイムがフルサポート

Javaは型消去(type erasure)

値型サポート(ボックス化なし)

Javaはプリミティブ型を型引数に指定できない

配列はジェネリックコレクション

Javaの配列はジェネリックではない

変性(共変・反変)を型の定義時に指定する

Javaでは型の利用時にワイルドカードで指定する

Page 4: 富山合同勉強会2015 ジェネリクス談義 C#編

ランタイムがフルサポート

コンパイル時にできることはすべて実行時(=リフレクション)でもできる実行時にできることはおおむねコンパイル時でもできる

実行時にキャストが発生しないので、パフォーマンスが(ちょっと)良い

Page 5: 富山合同勉強会2015 ジェネリクス談義 C#編

ランタイムがフルサポート

例: T型の型情報を取れる

class A{

public Type GetType<T>(){

return typeof(T);}

}

Javaで言うならT.class

Page 6: 富山合同勉強会2015 ジェネリクス談義 C#編

ランタイムがフルサポート

例: T型の配列をインスタンス化できる

class A{

public T[] ToArray<T>(T x, T y, T z){

return new T[] { x, y, z };}

}

Page 7: 富山合同勉強会2015 ジェネリクス談義 C#編

ランタイムがフルサポート

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<?>と書ける

Page 8: 富山合同勉強会2015 ジェネリクス談義 C#編

ランタイムがフルサポート

誤解の例:「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;

}}

Page 9: 富山合同勉強会2015 ジェネリクス談義 C#編

値型サポート(ボックス化なし)

ボックス化が発生しないのでパフォーマンスが(ちょっと)良い

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配列になる

Page 10: 富山合同勉強会2015 ジェネリクス談義 C#編

配列はジェネリックコレクション

配列を特別扱いすることが(すこし)減る

配列にも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();

Page 11: 富山合同勉強会2015 ジェネリクス談義 C#編

変性を型の定義時に指定する

変性(共変、反変、不変)

JavaもC#も、配列は共変

※なお、Javaには「共変戻り値」というものもある

string[] array1 = new string[] { "a", "b" };object[] array2 = array1;object first = array2[0];

stringの配列型はobjectの配列型と互換性がある

(取り出す時にアップキャストしても安全)

Page 12: 富山合同勉強会2015 ジェネリクス談義 C#編

変性を型の定義時に指定する

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>

Page 13: 富山合同勉強会2015 ジェネリクス談義 C#編

変性を型の定義時に指定する

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>

Page 14: 富山合同勉強会2015 ジェネリクス談義 C#編

変性を型の定義時に指定する

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>型と互換性がある

(取り出す時にアップキャストしても安全)

Page 15: 富山合同勉強会2015 ジェネリクス談義 C#編

C#と Java のジェネリクスの共通点

型制約

ある型Tの実体に対して何らかの処理を適用したい

何らかの処理を「安全」に適用するにはその型がどういうものか、前提知識が必要

型の前提知識=型制約

C#もJavaも、型Tに処理Mを適用できるかどうか判断する方法は、型階層のみ(型Aが型Bから派生しているかどうかを制約とする)

Page 16: 富山合同勉強会2015 ジェネリクス談義 C#編

まとめ

細かな違いはあるが、型システムを俯瞰すると、実はそんなに違わない

C# の戦略(だと私が想像するもの) Windowsネイティブ(C/C++)との相互運用性重視⇒値型の重視、パフォーマンス重視

他の.NET言語(VB.NET)との相互運用性重視⇒ランタイムとの同調、ランタイムがサポートしない機能を含めない

ランタイムとフレームワークとの一貫性⇒ランタイムを更新するときはF/Wも更新される?