SlideShare a Scribd company logo
1 of 167
C#や.NET Framework
がやっていること
岩永 信之
自己紹介

C#でぐぐれ
こんなロゴのサイトの中の人
プログラミングの魔導書 Vol.3にも何か書いてるよ
今日の内容
• C#や.NET Frameworkがやっていること
• どうしてそういう機能がある
• (自前で実装するなら)どうやればできる
• (自前でやる上で) 気を付ける必要がある点
趣旨
• 言語的に機能がないからって、やらなくていい
わけじゃない。
• C#とかが持ってる機能は必要だから追加されてる
• 自前で同様のことをやる方法も知ってて損ない
• 例えば…
• C言語でもOOPやってる人はやってた
• C言語でも仮想関数テーブル自前で書いたり
注意

170ページ
• 本来、1時間で話す内容じゃない
• 置いてきぼる気満々
• あんまり細かい説明しない(というかできない)
まずは
.NET Frameworkといえば

“Managed”
“Managed”
• .NET Frameworkの管理下にある
• メモリ管理
• メモリ解放
• 境界チェック
• ドメイン分離

• 型の管理
• メタデータ
• JIT
• PCL
Garbage Collection
メモリ リークをなくすために
.NET FrameworkのGC
• .NET FrameworkのGCはMark & Sweep
• 世代別 (3世代、Gen0~2)
• Compactionあり
• Backgroundスレッドで並列実行
Mark & Sweep
• ルート†から参照をたどる
ルート
ヒープ

Mark
使用中オブジェクト

Sweep

未使用オブジェクト

†スタックや静的フィールド中のオブジェクト
Compaction
• GC時にオブジェクトの位置を移動

隙間をなくすように移動

後ろが丸ごと空くので次のメモリ確保が楽

使用中オブジェクト
未使用オブジェクト
Generation
• GC時にオブジェクトの位置を移動

オブジェクトの寿命
統計的に
• 短いものはとことん短く
• 長いものはとことん長い

Gen0

Gen1
しばらくここは
ノータッチ

Gen0
この範囲でだけ
メモリ確保・GC

一度GCかかったオブジェクト
はしばらく放置
C++でのGC
• 通常、参照カウント

• shared_ptr
• 循環参照に気を付けて適宜weak_ptrを

• Mark & Sweep GCライブラリもあるけども
• 型がはっきりしないので保守的にならざるを得ない
• すべての数値をポインターとみなしてMark
Mark & Sweepと参照カウント
• 比較
• 一長一短ある
Mark & Sweep

参照カウント

メモリ確保

○ 末尾を見るだけ

× 空いている場所を探す
× カウント用の領域が追加
で必要

変数のコピー

○ ポインターのコピー

× ポインターのコピーに加
え、参照数のカウント
アップ

メモリ解放

× MarkやCompactionに
時間がかかる
× 負担が1か所に集中

○ カウントが0になった時
にdeleteするだけ
Throughput
• トータルの性能(throughput)はMark & Sweep
の方がいい
Mark & Sweep

メモリ確保

参照カウント

○ 末尾を見るだけ

× 空いている場所を探す
× カウント用の領域が追加
で必要

○ ポインターのコピー
変数のコピー 参照局所性も高くなって
キャッシュが効きやすい
メモリ解放

× MarkやCompactionに
時間がかかる
× 負担が1か所に集中

まとめてやる方がバラバラに
やるより効率がいい

× ポインターのコピーに加
え、参照数のカウント
アップ
頻度が高い操作なので
○ カウントが0になった時
ここの負担が大きいと
にdeleteするだけ
全体の性能落ちる
特に、スレッド安全を求めるときつい
たかがインクリメントでも、atomic性
保証するとそこそこの負担
短所の軽減
• 短所も、まったく打つ手がないわけじゃない
Mark & Sweep

参照カウント

メモリ確保

○ 末尾を見るだけ

× 空いている場所を探す
× カウント用の領域が追加
で必要

変数のコピー

○ ポインターのコピー

× ポインターのコピーに加
え、参照数のカウント
アップ

メモリ解放

.NET 4以降、Background
スレッド実行してる
× MarkやCompactionに
時間がかかる
× 負担が1か所に集中

○ カウントが0になった時
C++ 11的には
にdeleteするだけ
「move semantics活用
してね」
自前メモリ管理との混在
• スコープが短いならスタックにとればいいのに
• C#はclassは必ずヒープ、structはスタックになる
• 使い分けれない

• Mark & Sweep管理領域内のポインターを管理
外に渡すときには注意が必要
• Compactionでメモリ移動しないように
「ピン止め」が必要
• 結構ガベージ コレクションのパフォーマンス落とす
おまけ
• Pythonとか

• 参照カウントとMark & Sweepの併用
• ○ 循環参照問題避けつつ、負担を1か所に集中させない
• × 性能的には悪いとこどり

• Go
• 割当先を自動判別
• スコープ内で完結していたらスタックに
• そうでなければヒープに
• これはこれでスタックの浪費激しそうなんだけども…
• なので、可変長スタック持ってる
境界チェック
意図しないメモリ領域にはアクセスさせない
配列の境界チェック
• .NETの配列は厳しい
• 常に境界チェックしてる
配列 a
a[-1]
OutOfRange

a[length+1]
OutOfRange

• 要はbuffer overrun防止
• ちなみにJITの最適化で、明らかに境界を侵さない
ものは境界チェックを消す
unsafe
• C#にもポインターあるんだけども
安全でないコード
この中でだけポイ
ンター利用可能
var x = new[] { 1, 2, 3, 4, 5 };
unsafe
{
fixed(int* px = &x[0])
メモリ移動の防止
{
(ピン止め)
Console.WriteLine(px[100]);
}
}
範囲チェックなし
buffer overrunやりたい放題
unsafeでも制限付き
• class (参照型)はアドレス取れない
• 仮想メソッド テーブル(vtable)とか入ってるし
• 親クラス側のレイアウト変更の影響受けるし
class Class
var c
{
fixed
public int x;
fixed
public int y;
fixed
}
ピン止め必須
•
•

= new Class();
(void* p = &c) { }
// error
(void* p = &c.x) { } // OK
(void* p = &c.y) { } // OK
値型のメンバーのアドレスは取れる
オブジェクト自体のアドレスは取れ
ない
unsafeでも制限付き
• struct (値型)はアドレス取れる
• ただし、メンバーが全部値型の時のみ
• ↑「unmanaged型」と呼ぶ
struct UnmanagedStruct
{
public int x;
public int y;
}
メンバーが
全部値型

var u
void*
void*
void*

= new UnmanagedStruct();
p1 = &u;
// OK
p2 = &u.x; // OK
p3 = &u.y; // OK

無制限にアドレス取れる
unsafeでも制限付き
• struct (値型)であっても制限付き
• メンバーに1つでも参照型を含むとダメ
• ↑「managed型」と呼ぶ
struct ManagedStruct
{
public int x;
public string y;
}
メンバーに
参照型が1つ

var u
void*
void*
void*

= new ManagedStruct();
p1 = &u;
// error
p2 = &u.x; // OK
p3 = &u.y; // error

値型のメンバーのところ
だけはアドレス取れる
ポイント
• unsafe
• 危険なことは基本認めない
• 面倒な追加の構文を要求
• コンパイル オプションでも/unsafeの明記必須

• fixed
• ガベージ コレクションとの兼ね合い
• Compaction阻害になるので注意

• 「Unmanaged型」に限る
• 実装依存な部分(vtableとか)や、
型の定義側の変更が利用側に極力影響しないように
AppDomain
実行環境の分離
セキュリティ保証
コンポーネントの連携
• 他のアプリの機能を自分のアプリから呼びたい
• WordとかExcelとかのドキュメントを出力

• サーバー上に自分のアプリを配置したい
• IIS上に(ASP.NET)
• SQL Server上に

• (必ずしも)信用できない
• セキュリティ保証
• 参照先のクラッシュにアプリ/サーバー
が巻き込まれないように
• コンポーネントのバージョン アップ
AppDomain
• 単一プロセス内で、分離された複数の実行領域
(domain)を提供
プロセス

AppDomain 1
AppDomain 2

…

必ずしも信用できないコードを
安全に、動的に呼び出し

• 「分離」
• plugin: 動的なロード/アンロード
• security: AppDomainごとに異なる権限付与
• isolation: 別メモリ空間
ドメイン間には壁がある
• 別メモリ空間に分離されてる

AppDomain 1

AppDomain 2
互いに独立
ドメイン間の通信
• マーシャリング(marshaling)

• marshal (司令官、案内係)の命令通りにしか壁を超
えれない
• 司令がダメといったら通れない
• 司令の指示通りの形式にいったん
シリアライズしないといけない
AppDomain 1
AppDomain 2
ドメイン間の通信
• ダメな例

あるオブジェクト

AppDomain 1

これをBに渡し
たいとして

AppDomain 2
ドメイン間の通信
• ダメな例

AppDomain 1

AppDomain 2

shallow copyなんてしようもんなら
A側のメモリへの参照が残る
ドメイン間の通信
• よい例
一度シリアライズ

必ずdeep copy

{

{1, 2},
{3, 4},
{5, 6}

serialize
AppDomain 1

}

deserialize
AppDomain 2

この辺りがマーシャリング
.NETのマーシャリング
• 2種類

• marshal by ref
• 参照を渡すんだけど、メモリ領域は直接触れない
• メソッド越しにしか操作しちゃいけない
• 「このメソッドを呼んでくれ」っていうメッセージだけ
がドメインを超えてわたって、実際の実行は相手側ドメ
インで

• marshal by value
• 値をシリアライズして相手側に渡す
• 規定では、BinarySeralizerを使用
privateフィールドまで含めてリフレクションで
内部状態を取得してシリアライズ
.NETのマーシャリング(文字列)
• marshal by valueの場合でも文字列は特殊扱い
• コピーしない
• 参照をそのまま渡す
• immutableかつrange-check付きに作ってある
• COM (ネイティブ)に対して文字列を渡す時すらこの方式
(ただし、文字コード変換が不要な場合だけ)
その他のコスト
(マーシャリングのコスト以外にも)

• いくらかコストが
• 例えばライブラリの読み込み

Library X
AppDomain 1

Library X
AppDomain 2

別ドメインに全く同じライブラリを読み込んでも、
それぞれ別イメージが作られる
Global Assembly Cache (GAC)にあるDLLだけは別
GAC中のは同じメモリ イメージが共有される
C++でも
• COMなんかはマーシャリングやってる
• 逆にいうと、COMくらい仰々しいものが必要
• さもなくば、プロセス分離してプロセス間通信とか
やらなきゃいけない
• 安全だけどかなり高負荷
• RPCとか一時期流行ったけども
• ただの関数呼び出しに見えるコードで、内部的にプロセス
間通信するとか
メタデータ
型情報
動的リンクでのバージョン管理
メタデータ
• メタデータとは

• DLLにどういう型が含まれるかという情報
• 外部のどういうDLLを参照しているかの情報
• バージョン情報

• メタデータがあると
• プログラミング言語をまたげる
• 動的リンクでのバージョン管理
動的リンク
• ライブラリは共有する

アプリA

ライブラリX
version 1

アプリB

ライブラリY
version 1
動的リンク
• ライブラリ単体での差し替え
• セキュリティ ホールや、致命的なバグの修正
差し替え

アプリA

更新不要

ライブラリX
version 2

アプリB

ライブラリY
version 1
いまどきの事情
• ライブラリはアプリのパッケージに同梱
• バージョニングいるの?

アプリAパッケージ
アプリA

アプリBパッケージ

ライブラリX
ライブラリY

ライブラリX
アプリB

別バイナリ

ライブラリY
差分ダウンロード
※ Windowsストア アプリはこの仕組み持ってるらしい
アプリAパッケージ version 1
アプリA
version 1

アプリAパッケージ version 2

ライブラリX
version 1

アプリA
version 1

ライブラリY
version 1

ライブラリX
version 2
ライブラリY
version 1

差分

ライブラリX
version 2
バージョンアップ時

ダウンロード

アプリA ver.1
インストール機
アプリ間でライブラリ共有
※ Windowsストア アプリはこの仕組み持ってるらしい
アプリAパッケージ
アプリA

アプリBパッケージ
ライブラリX
version 1

ライブラリX
version 1

アプリB

ライブラリY
version 1

ライブラリY
version 1

差分

アプリB
アプリBインストール時
X、Yは同じものを共有
(ハード リンク作るだけ)

アプリA
インストール機
メタデータの作成
• COMの頃
• メタデータを自分で書いてた
(自動化するツール使あるけど)
• .tlb/.olbファイル

• .NET Framework
• .NET自体ががメタデータの規格を持ってる
• C#とかをコンパイルするだけで作られる
C++/CX
• C++ Component Extensions
• マイクロソフトのC++拡張
素の標準
C++コード
C++/CX

コンパイル

C++内で完結して使う分
にはオーバーヘッドなし

COMコード

COMを呼べる言語なら
何からでも呼べる

メタデータ
(winmd)

.NETのメタデータと互換
.NETから簡単に呼べる
JIT
(Just-in-Time compile)
バージョン アップ時の変更の影響を吸収
中間コードとJITコンパイル
• .NET Frameworkの中間言語
ビルド時に
コンパイル
高級言語
(C#など)

Just-in-Time
コンパイル

中間言語
ネイティブ
(IL)
コード
JITである必要ない
LLVMとかでも中間言語介してコンパイルしてる

• 高級言語のコンパイラーを作る人と、CPUごとの最
適化する人の分業化 ストア審査ではじくとか他にも手段はある
• セキュリティ チェックしやすくなったり
• 動的リンク時に、コード修正の影響をJITで吸収
例
• (C#で)こんな型があったとして
public struct Point
{
public int X;
public int Y;
public int Z;
}

• 整数のフィールドを3つ持つ
例
• こんなメソッドを書いたとする
static int GetVolume(Point p)
{
return p.X * p.Y * p.Z;
}

• フィールドの掛け算
IL
• C#コンパイル結果のIL
.method private hidebysig static int32
GetVolue(valuetype Point p) cil managed
{
.maxstack 8
型とかフィールド
IL_0000: ldarg.0
IL_0001: ldfld
int32 Point::X
の名前がそのまま
IL_0006: ldarg.0
残ってる
IL_0007: ldfld
int32 Point::Y
IL_000c: mul
型情報
IL_000d: ldarg.0
メタデータ
IL_000e: ldfld
int32 Point::Z
IL_0013: mul
IL_0014: ret
}
ネイティブ コード
• JIT結果 (x64の場合)
push
mov
cmp
je
call
mov
lea
imul
lea
imul
pop
ret

ebp
ebp,esp
dword ptr ds:[5011058h],0
00FE2A01
74B7AEA8
eax,dword ptr [ebp+8]
edx,[ebp+8]
eax,dword ptr [edx+4]
edx,[ebp+8]
eax,dword ptr [edx+8]
ebp
0Ch

4とか8とかの数値に
型情報は残らない
メモリ レイアウト
• この4とか8の意味

Point
X

public struct Point
{
public int X;
public int Y;
public int Z;
}

Y

4バイト

8バイト

Z

※レイアウトがどうなるかは環境依存
メモリ レイアウト
• この4とか8の意味
ILの時点までは名前
で参照してる
public struct Point

Point
X

{
public int X;
public int Y;
public int Z;
}

Y

4バイト

8バイト

ネイティブ コードは
レイアウトを見て
Z
数値で参照してる
※レイアウトがどうなるかは環境依存
数値でのフィールド参照
• C#で擬似的に書くと
static int GetVolume(Point p)
{
return p.X * p.Y * p.Z;
}

var pp = (byte*)&p;
var x = *((int*)pp);
var y = *((int*)(pp + 4));
var z = *((int*)(pp + 8));
return x * y * z;

4とか8とかの数値に

※これ、一応C#として有効なコード(unsafe)
変更してみる
• 大して影響しなさそうな
ほんの些細な変更をしてみる
public struct Point
{
public int X;
public int Y;
public int Z;
}

public struct Point
{
public int X;
public int Z;
public int Y;
}
フィールドの順序変更
その結果起きること
• メモリ レイアウトが変わる※
Point

Point

X

X

Y

Z

Z

Y
※ この例(フィールド変更)以外でも、仮想メソッド

とかいろいろレイアウトが変わるものがある

テーブル
ILレベルでの影響
• 影響なし
IL_0000:
IL_0001:
IL_0006:
IL_0007:
IL_000c:
IL_000d:
IL_000e:
IL_0013:
IL_0014:

ldarg.0
ldfld
ldarg.0
ldfld
mul
ldarg.0
ldfld
mul
ret

int32 Point::X
int32 Point::Y

int32

名前で参照してるん
だから特に影響ない
Point::Z
JITが吸収してくれる
ネイティブ レベルでの影響
• ここで影響が出る
push
mov
cmp
je
call
mov
lea
imul
lea
imul
pop
ret

ebp
ebp,esp
dword ptr ds:[5011058h],0
00FE2A01
74B7AEA8
eax,dword ptr [ebp+8]
edx,[ebp+8]
eax,dword ptr [edx+4]
8
edx,[ebp+8]
更新が必要
4
eax,dword ptr [edx+8]
ebp
0Ch
利用側の再コンパイルが必要
ライブラリ側だけの差し替えじゃダメ
ただし…
• この役割に焦点を当てるなら…
• 毎回毎回JITする必要ない
• 全部が全部ILな必要ない
Ngen
• Ngen.exe
• Native Image Generator
• ILを事前にネイティブ化するためのツール
• 自前管理が必要
• アプリのインストーラー※とかを作って明示的に呼び出し
• 参照しているライブラリが更新された時には呼びなおす
必要あり

• かなり面倒なのでアプリを
Ngenすることはめったにない
• .NET自体が標準ライブラリの
高速化のために使ってる
※

要するに、JITの負担を起動時じゃなくてインストール時に前倒しする
Auto-Ngen
• .NET Framework 4.5以降なら

• NgenがWindowsサービスとして常に動いてる
• アイドル時に動作

• 利用頻度の高いものを自動的にNgen
• デスクトップ アプリの場合はGACアセンブリのみ
• Windowsストア アプリの場合はすべてのアセンブリ

• よく使うアプリの起動はだいぶ早くなる
• インストール直後の起動は相変わらず遅い
MDIL (ネイティブのおさらい)
• おさらい: ネイティブ コードだと
push
mov
cmp
je
call
mov
lea
imul
lea
imul
pop
ret

ebp
ebp,esp
dword ptr ds:[5011058h],0
00FE2A01
参照しているライブラリ
74B7AEA8
eax,dword ptr [ebp+8] のレイアウトが変わった
edx,[ebp+8]
時に再コンパイルが必要
eax,dword ptr [edx+4]
edx,[ebp+8]
eax,dword ptr [edx+8]
ebp
0Ch
MDIL (部分的にネイティブ化)
• じゃあ、こんな形式があればいいんじゃ?
push
mov
cmp
je
call
mov
lea
imul
lea
imul
pop
ret

ほぼネイティブ

ebp
レイアウトのところ
ebp,esp
dword ptr ds:[5011058h],0 だけ抽象的に型情報
00FE2A01
を残しておく
74B7AEA8
eax,dword ptr [ebp+8]Point::X
int32
edx,[ebp+8]
eax,dword ptr [edx+4]Point::Y
int32
edx,[ebp+8]
eax,dword ptr [edx+8]Point::Z
int32
ebp
0Ch
MDIL (Compile in the Cloud)
• 実際、Windows Phoneのストアでは
そういう形式が使われている: MDIL
(Machine Dependent Intermediate Language)
C#コード
C#コンパイラー

開発環境でILにコンパイル

MDILコンパイラー

Windowsストア サーバー
上でILをMDIL化
(Compile in the Cloud)

IL

MDIL
リンカー
ネイティブ コード

Windows Phone実機上では
レイアウトの解決(リンク)だけ行う

• インストール直後の起動も安心
C++だと
• 動的にやろうと思ったら、結局、COMとか
WinRTになってく
• いったんQueryInterfaceみたいなの挟む
• 結構でっかいオーバーヘッド†

† WinRTはオーバーヘッド解消の仕組みも持ってて、
C++で完結してる限りには普通の仮想関数呼び出しになる
他言語から使う場合にはCOMアクセスになる
C++だと
• 静的な話で言うと

ヘッダーファイルを変更したら
利用側の再コンパイルが必要
Point.h
struct Point
{
double x;
double y;
};

struct Point 要 再コンパイル
{
double radius;
double theta;
};
なので
• setter/getter使う
• pimpl使う

Point.cpp (実装その1)

struct PointImpl;

struct PointImpl
{
double x;
double y;
};

class Point
{
public:
double get_x();
double get_y();
private:
PointImpl* impl;
};

double Point::get_x()
{
return impl->x;
}
double Point::get_y()
{
return impl->y;
}

Point.h
なので
Point.cpp (実装その2)
こっちだけの変更なら、
setter/getter使う
利用側の再コンパイル不要

•
• pimpl使う
Point.h

変更なし

struct PointImpl;

class Point
{
public:
double get_x();
double get_y();
private:
PointImpl* impl;
};

struct PointImpl
{
double radius;
double theta;
};
double Point::get_x()
{
return impl->radius
* cos(impl->theta);
}
double Point::get_y()
{
return impl->radius
* sin(impl->theta);
}
Portable Class Library
複数の「標準ライブラリ」
いろんな実行環境の共通部分
標準ライブラリ
• マイクロソフト製品だけでも…
デスクトップ
クライアント アプリ
この共通部分だけを
「標準」にすべき?
共通部分

サーバー アプリ

Phone/タブレット
クライアント アプリ
標準ライブラリ
• まして今、Xamarin (Mono)

• Xamarin.iOS、Xamarin.Android
Windows
タブレット/Phone

Windows
デスクトップ

Windows
サーバー

iOS

Android

この共通部分だけを
「標準」にすべき?

Linux
サーバー
.NET FrameworkとMono
コンパイラー

.NET
Framework

実行環境

C#
VB

CLR

F#

ライブラリ

.NET Full
プロファイル
.NET Core
プロファイル
.NET Full
(サブセット)

Mono

C#

Mono
ランタイム

iOS向け
プロファイル
Android向け
プロファイル

実行環境に互換性
があっても

標準で使える
ライブラリが違う
どこでも使えそうに見えても…
• 例えばファイル システム
• 最近のWindowsはいろんなプロパティを持ってる
• 画像ファイルなんかだと:
サムネイル画像、撮影日時、画像サイズ、タグ
• 検索インデックスも張ってる
こういうものも標準に含めたいか?

• ストア アプリだとファイル システムすら制限下
• ユーザーの許可がないファイルには触れない
一番制限がきつい環境に合わせて標準ライブラリを作るべき?
汎用 VS 特定環境

どこでも動く

特定環境で動く

• 最大公約数
• 動作保証に時間が
かかる

• 高機能
• 早く提供できる
汎用 VS 特定環境

どこでも動く
• 最大公約数
• 動作保証に時間が
かかる

C++だと

• 標準化にかかってる時間
特定環境で動く
• ライブラリのソースコードをとってきたは
• 高機能
いいけど自分の環境での動作確認も大変
• 早く提供できる
Portable Class Library
• 実行環境ごとに
別の「標準ライブラリ」
メタデータを用意
• ライブラリ側でどの
実行環境をターゲットに
するか選ぶ

実行環境ごとに
別メタデータを提供

ターゲット選択
Portable Class Library
• 例: 2環境

共通部分
Portable Class Library
• 例: 3環境

共通部分
C++
• ターゲット広すぎてより一層大変だけど
• メタデータ持ってない言語でやるのも厳しい
• #ifdef?
さて
もうすでに約80ページ
ここまで/ここから
• ここまで: .NETのインフラ的なところ
• いわゆる“Managed”
• C# 1.0の頃からある基本機能
• 所詮、既存言語をManagedにした程度

• ここから: C# 2.0以降の追加機能
C#の歴史

C# 4.0
C# 3.0
C# 2.0
C# 1.0

• Dynamic

C# 5.0
• Async
• WinRT

• LINQ

• Generics

• Managed

※VB 7~11の歴史でもある
ジェネリック
C# 2.0
C++で言うところのtemplate
C++ templateと.NET Genericsの違い
C++ templateとC# generics
C++ template

C# generics

高機能マクロみたいなもので、コン
パイル時に全部展開される

.NETの中間言語/型情報レベルで対応
JIT時に展開

vector<T>とvector<U>で別コード
が生成される

List<T>とList<U>で同じコードを
共有(参照型の場合)

すべてヘッダーに書く必要がある
変更すると利用側も再コンパイル

内部を変更しても(publicなところが
変わらない限り)利用側に影響なし

コンパイル時に
すべてやる方針

メタデータ/JITで話した要件
• 言語をまたげる
• 動的リンク
• バージョン管理
• 利用側に影響与えない
.NET的にはこれを守るのが最重要
もしC++でgenericsやるなら
(動的には無理だけど、せめて静的に)

• いったん汎用クラスを作る
class void_vector
{
public:
void* operator[](int index);
void add(void* item);
void remove(void* item);
… 後略 …
いったんvoid*な
汎用クラスを作る
もしC++でgenericsやるなら
(動的には無理だけど、せめて静的に)

• キャストだけtemplateで
template<typename T>
class vector<T>
{
さっき作ったvoid*のクラス
private:
void_vector impl; キャストするだけの薄いtemplateクラスを作る
要素の型が保証されるのでstatic_castでOK
public:
T* operator[](int index)
{
return static_cast<T*>(impl[index]);
}
void add(T* item) { impl.add(item); }
void remove(T* item) { impl.remove(item); }
};
もしC++でgenericsやるなら
• このやり方(汎用クラス+templateでキャスト)
なら
• vector<T>とvector<U>でコード共有可能
• バイナリ サイズ小さくできる

• (pimplを併用すれば)内部実装を変更しても、利用
側の再コンパイル不要
• コンパイル時間だいぶ短くできる
とはいえ、いろいろ窮屈
• genericsでは、インターフェイス制約かけない
とメソッドすら呼べない
static Type Max<Type>(Type a, Type b)
{
return a.CompareTo(b) > 0 ? a : b;
}
コンパイル エラー
そんなメソッド知らない
正しくは
static Type Max<Type>(Type a, Type b)
where Type : IComparable
インターフェイス制約
{
return a.CompareTo(b) > 0 ? a : b;
}
IComparable.CompareTo
とはいえ、いろいろ窮屈
• 特に困るのが演算子使えないこと
• 実体は静的メソッドなので
static T Sum<T>(T[] array)
{
T sum = default(T);
foreach (var x in array) sum += x;
return sum;
}
コンパイル エラー
演算子定義されてない
とはいえ、いろいろ窮屈
• dynamicでごまかせなくはないけども…
• 性能出ないので最後の手段
static T Sum<T>(T[] array)
{
dynamic sum = default(T);
foreach (var x in array) sum += x;
return sum;
}
実行時コード生成で
+演算子が呼ばれる
C++/CX
• C++/CXはジェネリックを持ってる

• genericキーワード
• メタデータ(winmd)上は.NETのジェネリックと互換
• かなり制限きつい
• 実装(genericなクラス)はpublicにできない
• private/internalなクラスはtemplateで展開
• ユーザー定義のインターフェイス、デリゲートも
genericな場合はpublicにできない
イテレーター
C# 2.0
イテレーター生成用の構文
イテレーター ブロック
• C++的な意味のイテレーターの生成を楽にする
std::vector<int> v{ 1, 2, 3, 4 };
for (auto p = v.begin(); p != v.end(); ++p)
std::cout << *p << std::endl;
こういうの
• 使う側(forループ側)は楽でいいんだけども
• 実装側(vector_iteratorの中身)はめんどくさい
これを楽にする
例
• substringの列挙
実装側
static IEnumerable<string> GetSubstrings(string s)
{
for (var len = s.Length; len >= 1; len--)
for (var i = 0; i <= s.Length - len; i++)
yield return s.Substring(i, len);
}
イテレーター ブロック†
(= yield returnを持つ関数ブロック)

使う側
foreach (var x in GetSubstrings("abcd"))
Console.WriteLine(x);
内部実装(全体像)
• クラス生成

class SubstringEnumerable : IEnumerator<string>, IEnumerable<string>
{
readonly string _s;
int _len;
int _i;
int _state = 0;
public SubstringEnumerable(string s) { _s = s; }
public string Current { get; private set; }
public bool MoveNext()
{
if (_state == 1) goto STATE1;
if (_state == -1) goto END;
_state = 1;
_len = _s.Length;
LOOP1BEGIN: ;
if (!(_len >= 1)) goto LOOP1END;
_i = 0;
LOOP2BEGIN: ;
if (!(_i <= _s.Length - _len)) goto LOOP2END;
_state = 1;
Current = _s.Substring(_i, _len);
return true;
STATE1: ;
_i++;
goto LOOP2BEGIN;
LOOP2END: ;
_len--;
goto LOOP1BEGIN;
LOOP1END: ;
_state = -1;
END: ;
return false;
}

static IEnumerable<string> GetSubstrings(string s)
{
for (var len = s.Length; len >= 1; len--)
for (var i = 0; i <= s.Length - len; i++)
yield return s.Substring(i, len);
}

public void Reset() { throw new NotImplementedException(); }
public void Dispose() { }
object IEnumerator.Current { get { return Current; } }
public IEnumerator<string> GetEnumerator()
{
if(_state == 0) return this;
else return new SubstringEnumerable(_s).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
内部実装(ローカル変数)
• ローカル変数 → フィールド

static IEnumerable<string> GetSubstrings(string s)
{
for (var len = s.Length; len >= 1; len--)
for (var i = 0; i <= s.Length - len; i++)
yield return s.Substring(i, len);
}

class SubstringEnumerable : IEnumera
{
readonly string _s;
int _len;
int _i;
int _state = 0;

public SubstringEnumerable(strin

public string Current { get; pri

public bool MoveNext()
{
if (_state == 1) goto STATE1
if (_state == -1) goto END;
内部実装(yield return)
• yield return → 状態記録、return、caseラベル
• 中断と再開
static IEnumerable<string> GetSubstrings(string s)
{
for (var len = s.Length; len >= 1; len--)
for (var i = 0; i <= s.Length - len; i++)
yield return s.Substring(i, len);
}

if (_state == 1) goto STATE1;
if (_state == -1) goto END;
_state = 1;
_len = _s.Length;
LOOP1BEGIN: ;
if (!(_len >= 1)) goto LOOP1END;
_i = 0;
LOOP2BEGIN: ;
if (!(_i <= _s.Length - _len)) go
_state = 1;
Current = _s.Substring(_i, _len);
return true;
STATE1: ;
_i++;
goto LOOP2BEGIN;
内部実装(yield return)
• yield returnの部分、意味合いとしては†
switchで囲う

yield return以外の
場所はほぼ同じ
static IEnumerable<string> GetSubstrings(string s)
{
for (var len = s.Length; len >= 1; len--)
for (var i = 0; i <= s.Length - len; i++)
yield return s.Substring(i, len);
}

yield return x;

switch(_state)
{
case 0:
for (_len = _s.Length; _len >= 1; _len--)
for (_i = 0; _i <= _s.Length - _len; _i++)
{
_state = 1;
Current = _s.Substring(_i, _len);
return true;
case 1:;
}
_state = -1;
}

_state = 1;
Current = x;
return true;
case 1:;

† forループ内にラベル張れないからさっきみたいな複雑なコードになるけども
内部実装(中断と再開)
• yield returnの部分、意味合いとしては
• 中断と再開
yield return x;

状態の記録
_state = 1;
Current = x;
return true;
case 1:;

現在の値の保持
復帰用のラベル
機械的な置き換えでできる
• C++でも割と簡単
class substring_iterator
{
private:
const std::string& _s;
int _len;
int _i;
std::string _current;
int _state;
public:
substring_iterator(const std::string& s) : _s(s), _state(0) {}
std::string current() { return _current; }
bool move_next()
{
switch (_state)
{
case 0:;
for (_len = _s.length(); _len >= 1; _len--)
for (_i = 0; _i <= _s.length() - _len; _i++)
{
_state = 1;
_current = _s.substr(_i, _len);
return true;
case 1:;
}
_state = -1;
default:;
}
return false;
}

gotoとかcaseラベルの制限が
ゆるいので、むしろ簡単
機械的置き換えといえば
• マクロを定義
#define BEGIN_ITERATOR
switch(_state)
{
case 0:
#define END_ITERATOR
_state = -1;
default:;
}
return false;

#define YIELD(STATE, VALUE)
_state = STATE;
_current = VALUE;
return true;
case STATE:;
機械的置き換えといえば
• マクロを使って
bool move_next()
{
BEGIN_ITERATOR
for (_len = _s.length(); _len >= 1; _len--)
for (_i = 0; _i <= _s.length() - _len; _i++)
{
YIELD(1, _s.substr(_i, _len))
}
END_ITERATOR
}
他の言語だと
• 最近の言語は結構似た機能持ってる
• ジェネレーター(generator)って呼ばれることが多い
• 実装方式:
• スタック丸ごとキャプチャしてしまうもの
• 中断時に記録、再開時に復元

• 式を継続渡しスタイル(CPS)に変換してしまうもの
• スレッド使う
• concurrent queue使って、yield returnのところでenqueue
• さすがに性能的に論外だけども
ちなみに
• コーディング面接で有名な某社の社員さん曰く、

「 アルゴリズムの問題はかなりの割合、

イテレーターを使うとあっさり書ける 」

• 例に使ったのsubstring列挙も割とよく出てくるパ
ターン
• 中断と再開って大事
C++ 1y
• C++にもジェネレーターが載るかも
• C++ 17に向けて標準化案出てる
• C++/CX向けの実装を元にMSが提案
• async/awaitのついで
sequence<int> range(int low, int high) resumable
{
for(int i = low; i <= high; ++i)
{
yield i;
}
}
LINQ
(Language Integrated
Query)
C# 3.0
データ処理の直行化
LINQ
• データ処理を言語統合
• ほとんどはライブラリで実現
• STLの<algorithm>とかBoost.RangeのAdaptersみたいなも
の
var source = new[] { 1, 2, 3, 4, 5 };
var filtered = source
.Where(x => x <= 3) // 1, 2, 3
.Select(x => x * x); // 1, 4, 9
System.Linq.Enumerable.Whereメソッドと
System.Linq.Enumerable.Selectメソッド
が呼ばれるだけ
データ処理の直行化
• まあ、データの入力、加工、出力は分けましょ
うという話
入力

加工

配列から
{ 1, 2, 3, … }

1, 2, 3 変換
x => x * x 1, 4, 9

データベースから
SELECT x FROM t
ファイルから
File.Read…

ユーザー入力から
Console.Read…

1, 2, 3 選択
x => x < 3 1, 2
1, 2, 3 グループ化
x => x % 2 {1, 3},
{2}

出力

配列に
ToArray()
データベースに
INSERT INTO t
ファイルに
File. Write…

コンソールに
Console.Write…
データ処理の直行化
• 掛け算を足し算に
Lパターン

Mパターン

Nパターン

入力

加工

出力

配列から
{ 1, 2, 3, … }

1, 2, 3 変換
x => x * x 1, 4, 9

配列に
ToArray()

データベースから
データベースに
1, 2, 3 選択
SELECT 分けて作らないと L×M×N 通りのパターン
x FROM t
INSERT INTO t
x => x < 3 1, 2
分けて作ると L+M+N 通り
ファイルから
ファイルに
File.Read…
File. Write…
1, 2, 3 グループ化
x => x % 2 {1, 3},
ユーザー入力から
コンソールに
{2}
Console.Read…
Console.Write…
関連するC#の言語機能
• C# 3.0
• ラムダ式
• 匿名型
• 拡張メソッド
ラムダ式
• 匿名関数を簡単に書ける
source.Select(x => x * x);
(シーケンスの全要素を二乗)

• C++のラムダ式と比べてシンプルなのは
• 型推論がやりやすい
• const&とかがない
• ガベージ コレクションがあるし、変数キャプチャ
し放題
匿名型
• 1か所でしか使わないような型は作らなくてい
い
source.GroupBy(p => new { p.X, p.Y });
(XとYでグループ化)

• immutableなクラスを自動生成
• GetHashCode、等値比較、ToStringを完備
拡張メソッド
• 静的メソッドを後置き記法で書ける
source.Select(x => x * x);
同じ意味

System.Linq.Enumerable.Select(
source, x => x * x);
C++でも関数オブジェクトに
対する演算子オーバーロード
似たことできなくはない

• 単に語順を変えるだけ
• 静的メソッドはpure functionである限り無害
• pure = 状態を持たない
同じ入力を与えたら常に同じ結果が帰ってくる
dynamic
C# 4.0
動的コード生成
ダック タイピング
dynamic型
• C# 4.0のdynamic型
• 結構用途が限られてて
• できること
• 静的な型に対する動的コード生成
• DLR言語との連携

• できるけど過剰スペックなこと
• JSONみたいなスキーマレスなデータ読み書き

• できないこと
• メタプログラミング
• C#のスクリプト的実行
主なターゲットは静的な型
• .NETの型情報に基づいた動的コード生成

• IDynamicMetaObjectProviderを実装すれば型情報な
くてもコード生成できるけども、あまりやる人いな
い

• 利用例
• 多重ディスパッチとか
• ダック タイピングとか
多重ディスパッチ
• 静的な型に対して
class Base { }
class A : Base { }
class B : Base { }
多重ディスパッチ
• x, yの両方の型で動的に分岐
• 仮想メソッドでは(素直には)できない
static class Extensions
{
public static string Dispatch(this Base x, Base y)
{
動的な呼び出し
return (string)X((dynamic)x, (dynamic)y);
}
static string X(A x, A y) { return "A - A"; }
static string X(A x, B y) { return "A - B"; }
static string X(Base x, Base y) { return "others"; }
}
多重ディスパッチ
• 呼び出し例
static void Main()
{
Dispatch(new A(),
Dispatch(new A(),
Dispatch(new B(),
Dispatch(new B(),
}

new
new
new
new

A());
B());
B());
A());

//
//
//
//

A - A
A - B
others
others

static void Dispatch(Base x, Base y)
{
Console.WriteLine(x.Dispatch(y));
}
ダック タイピング
• 静的な型に対して
class Point
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return "Point(" + X + ", " + Y + ")";
}
}
ダック タイピング
• 同じ名前のメンバーを持つ別の型をまとめて処
理
static class Extensions
{
public static void CopyTo<T, U>(this T p, U q)
{
dynamic x = p;
dynamic y = q;
y.X = x.X;
X, Yを持つ任意
y.Y = x.Y;
の型に使える
}
}
ダック タイピング
• 呼び出し例
var p = new Point();
new { X = 10, Y = 20 }.CopyTo(p);
Console.WriteLine(p);
DLR連携
• DLR (Dynamic Language Runtime)
• 例: IronPython
var py = IronPython.Hosting.Python.CreateEngine();
dynamic p = py.Execute("['a', 'b', 1, 2]");

for (var i = 0; i < 4; i++)
Console.WriteLine(p[i]);

• DLRは内部的に.NETの型を生成してるので内部実装
的にはこれも「静的な型に対する動的コード生成」
dynamicの内部実装
• コード生成結果
実際にはobject

object X(object x)
{
if (_site1 == null)
dynamic X(dynamic x)
{
{
_site1 = CallSite<Func<CallSite, o
return x.X;
Binder.GetMember(CSharpBinderF
}
動的コード生成
new CSharpArgumentInfo[]
用の情報
{
CSharpArgumentInfo.Create(
これが本体
}));
}
return _site1.Target(_site1, x);
}
CallSite.Targetの中身
• メソッドを動的コード生成してる
• 生成したメソッドはキャッシュして持っておく
(inline method cache)
_site1.Targetの初期状態
static object _anonymous(CallSite site, object x)
{
return site.Targetを更新する処理
}
CallSite.Targetの中身
• メソッドを動的コード生成してる
• 生成したメソッドはキャッシュして持っておく
(inline method cache)
メソッドXにPoint型のインスタンスを渡した後
static object _anonymous(CallSite site, object x)
{
1行追加
if (x is Point) return ((Point)x).X;
単なる型判定+キャスト
else return site.Targetを更新する処理
}

• 同じ型に対して何度も呼ぶ分には高性能
CallSite.Targetの中身
• メソッドを動的コード生成してる
• 生成したメソッドはキャッシュして持っておく
(inline method cache)
さらに、メソッドXにVector3d型のインスタンスを渡した後
static object _anonymous(CallSite site, object x)
{
if (x is Point) return ((Point)x).X;
if (x is Vector3D) return ((Vector3D)x).X; もう1行追加
else return site.Targetを更新する処理
}
ちなみに、最近はスクリプト言語でも、
内部的に型を作って、inline method cache
で高速化してる
“heavy” dynamic
• 用途によっては高性能なものの…
• 多くの場合、過剰スペック
• できるけど過剰スペックな例
• JSONみたいなスキーマレスなデータ読み書き

• もっと”light-weight”なdynamicが必要
(今のC#にはない。要望としてはある)
例: 規約ベースの置き換え
x.X;
x.Get("X");
x.M();
x.Invoke("M");
できないこと
• メンバー名がコンパイル時に既知でないとダメ
dynamic x = p;
dynamic y = q;
y.X = x.X;
コンパイル時に既知
y.Y = x.Y;

• なのでメタプログラミングには使えない
• そのためにはもっと低レイヤーなAPI使う
• IL Emit
• Expression Tree
• Roslyn
コード生成API
高級言語
(C#)

Roslyn†

parse
構文木

式ツリー
(System.Linq.Expressions)

emit
ILGenerator

IL

JIT
ネイティブ
コード

† 新しいC#コンパイラーのコードネーム
動的なコンパイルも可能
コード生成にかかる時間
• あんまり動的にやりたくない
ある環境で、あるコードの生成結果の例

かかった時間[ミリ秒]
ILGenerator
(JIT)

39.89

Expressions
(emit→JIT)

67.94

Roslyn
(parse→emit→JIT)

4314

倍遅い
2桁遅い

• 当たり前だけどparseは遅い
• quoteほしい
• できればコンパイル時生成でメタプログラミングしたい
async/await
C# 5.0
非同期処理
非同期処理しんどい
• 普通に非同期処理やったらコールバック地獄
• begin/end地獄
• イベント地獄
• then地獄(ContinueWith地獄)

x.BeginX(args, state, ar =>
x.XAsync()
{
.ContinueWith(t =>
var result = x.EndX();
{
…
var result = t.Result;
x.XCompleted += (s, arg) =>
});
…
{
};
非同期呼び出しが1回だから
var result = arg.Result;
この程度で済んでる
…
});
x.XAsync();
面倒な非同期処理の例
• 複数の確認ダイアログ表示
確認フロー

ゲームで
アイテムを
合成します

確認 1
チェック

レア アイテムですよ?

Yes

合成強化済みですよ?

Yes

もう強化限界ですよ?

No

確認 2
チェック

ユーザーから
の入力待ちも
非同期処理

Yes

No

確認 3
チェック
No

結果表示
同期処理
if (Check1.IsChecked)
{
var result = Dialog.ShowDialog("確認 1", "1つ目の確認作業");
if (!result) return false;
}
if (Check2.IsChecked)
{
var result = Dialog.ShowDialog("確認 2", "2つ目の確認作業");
if (!result) return false;
}
if (Check3.IsChecked)
{
var result = Dialog.ShowDialog("確認 3", "3つ目の確認作業");
if (!result) return false;
}
return true;
非同期処理 (旧)
if (Check1.IsChecked)
{
Dialog.BeginShowDialog("確認 1", "1つ目の確認作業", result =>
{
if (!result)
{
onComplete(false);
return;
}
if (.Check2.IsChecked)
{
Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result2 =>
{
if (!result2)
{
onComplete(false);
return;
}
if (Check3.IsChecked)
{
Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>
{
onComplete(result3);
});
}
else
onComplete(true);
});
}
else if (Check3.IsChecked)
{
Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>
{
onComplete(result3);
});
}
else
onComplete(true);
});
}
else if (Check2.IsChecked)
{
Dialog.BeginShowDialog("確認 2", "2つ目の確認作業", result =>
{
if (!result)
{
onComplete(false);
return;
}
if (Check3.IsChecked)
{
Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>
{
onComplete(result);
});
}
else
onComplete(true);
});
}
else if (Check3.IsChecked)
{
Dialog.BeginShowDialog("確認 3", "3つ目の確認作業", result3 =>
{
onComplete(result3);
});
}
else
onComplete(true);

• 画面に収まるように
フォント サイズ調整
• 4pt
• ほんの84行ほど

• ちなみに
• 部分部分を関数化して多
少は整理できる
• ダイアログ3つだからまだ
この程度で済む
非同期処理(C# 5.0)
if (this.Check1.IsChecked ?? false)
{
var result = await Dialog.ShowDialogAsync("確認 1", "1つ目の確認作業");
if (!result) return false;
}
if (this.Check2.IsChecked ?? false)
{
var result = await Dialog.ShowDialogAsync("確認 2", "2つ目の確認作業");
if (!result) return false;
}
if (this.Check3.IsChecked ?? false)
{
var result = await Dialog.ShowDialogAsync("確認 3", "3つ目の確認作業");
if (!result) return false;
}
return true;

• 同期処理と比べてawait演算子が増えただけ
• ダイアログの数が増えても平気
イテレーターと似た仕組み
• イテレーター(ジェネレーター)があれば、割と
単純なラッパーで非同期処理も可能
• 要は、「中断と再開」
• 例えば、TypeScriptでは
• 現状でもawaitのMS社内実装は持ってる
• いまだと、生成されるJavaScriptが悲惨すぎて大変
• EcmaScriptにジェネレーターが実装されてから、
TypeScriptにawaitを追加する予定
参考: C#のイテレーター(再)
• 中断と再開

class MethodEnumerator : IEnumerator<int>
{
public int Current { get; private set; }
private int _state = 0;
public bool MoveNext()
{
switch (_state)
{
case 0:

状態の記録

Current = 1;
Current = 1;
_state = 1;
_state = 1;
return true;
中断
return 1:
case true;
case 1: = 2;
Current
_state = 2;
return true; 再開用のラベル

IEnumerable<int> Method()
{
yield return 1;

yield return 2;

case 2:

}
}

}

}

default:
return false;
基本的な考え方
• 概念としては イテレーター+継続呼び出し
状態の記録

async Task<int> Method()
{
var x = await task1;
var y = await task2;
}

非同期処理が終

_state = 1;
わったら続きから
if (!task1.IsCompleted)
呼び出してもらう
{
task1.ContinueWith(a);
return;
中断
}
再開用のラベル
case 1:
var x = task1.Result;

結果の受け取り
実際の展開結果
• 実際はもう少し複雑

• Awaiterというものを介する(Awaitableパターン)
_state = 1;
var awaiter1 = task1.GetAwaiter();
if (!awaiter1.IsCompleted)
{
awaiter1.OnCompleted(a); • Awaiterを自作することで、
awaitの挙動を変更可能
return;
}
• Task以外もawait可能
case 1:
var x = awaiter1.GetResult();
C++ 1y
• C++にもawaitが載るかも

• C++ 17に向けて標準化案出てる
• C++/CX向けの実装を元にMSが提案
• 前述の通り、ついでにジェネレーターも
future<void> f(stream str) async
{
shared_ptr<vector> buf = ...;
int count = await str.read(512, buf);
return count + 11;
}
もう少し低レイヤーな話
• Taskクラス
async Task<int> Method()
{
var x = await task1;
var y = await task2;
}

_state = 1;
if (!task1.IsCompleted)
{
task1.ContinueWith(a);
return;
}
case 1:
var x = task1.Result;

• スレッド機能だけあればいいわけじゃない
• Task Pool、同期コンテキストとか
スレッド
• 非同期処理の最も低レイヤーな部分
• ただし、高負荷
for (int i = 0; i < 1000; i++)
{
var t = new Thread(Worker);
t.Start();
}

1000個の処理を同時実行

• 細々と大量の処理(タスク)をこなすには向かない
• 切り替え(コンテキスト スイッチ)のコストが高すぎる
2種類のマルチタスク
プリエンプティブ
•
•
•
•

ハードウェア タイマーを使って強制割り込み
OSが特権的にスレッド切り替えを行う
利点: 公平(どんなタスクも等しくOSに制御奪われる)
欠点: 高負荷(切り替えコストと使用リソース量)

協調的※
•
•
•
•

各タスクが責任を持って終了する
1つのタスクが終わるまで次のタスクは始まらない
利点: 低負荷
欠点: 不公平(1タスクの裏切りが、全体をフリーズさせる)
※cooperative
スレッド プール
• スレッドを可能な限り使いまわす仕組み
• プリエンプティブなスレッド数本の上に
• 協調的なタスク キューを用意
スレッド プール

キュー
新規タスク

タスク1
タスク2

…

タスクは一度
キューに溜める

数本のスレッド
だけ用意

空いているスレッドを探して実行
(長時間空かない時だけ新規スレッド作成)
ちなみに、std::futureはThread Poolな実装になってるはず
スレッド プールの向上
• Work Stealing Queue
• lock-free実装なローカル キュー
• できる限りスレッド切り替えが起きないように
グローバル
キュー

①
スレッドごとに
キューを持つ

ローカル
キュー1

ローカル
キュー2

スレッド1

スレッド2
②
ローカル キュー
が空のとき、
他のスレッドから
タスクを奪取
シングル スレッド必須なもの
• スレッド安全なコードは高コスト
• いっそ、単一スレッド動作を前提に
• 典型例はGUI
• C#/.NETに限らずたいていのGUIフレームワークはシング
ル スレッド動作
• 低レイヤーAPI(DirectXとかOpenGLとか)も、1つのス
レッドからしか扱えない
典型例: UIスレッド
• GUIは単一スレッド動作(UIスレッド)
• ユーザーからの入力受け付け
• 画面の更新
ユーザー
からの入力

UIスレッド

グラフィック

他のスレッド

更新
OK

処理中は
応答不可

他のスレッドから
は更新不可
矛盾
シングル スレッド推奨

単一スレッドからしか
UI更新できない
OK

そのスレッドを止める
とUIフリーズ
マルチ スレッド推奨
解決策
1. スレッド プールで重たい処理
2. UIスレッドに処理を戻してからUI更新
UIスレッド

他のスレッド

Task.Run

いったんUIスレッド
にメッセージを渡す

重たい処理
Dispatcher.Invoke
更新
OK

渡す役割を担うのが
ディスパッチャー※
※

dispatcher: 配送者

他のGUIフレームワークだとevent queueとかhandlerとかいう
名前で提供されたりするものの、やってることは一緒
おまけ: immutable
• 並列処理といえばimmutableだけども
• 書き換えが起こらないなら複数のスレッドで共有し
ても安全

• この用途だと、C++のconstは不十分
• あれは参照渡しを安全に行う用であって
• 呼び出し元の側ではconstとは限らず、書き換わる
可能性あり
• あと、mutable修飾子を付ければconstなオブジェク
トすら書き換えれる
Compiler as a Service
Future of C#, C# 6.0
コンパイラーの内部データを活用
Compiler as a Service
• 今、C#はコンパイラーをフルスクラッチで作
り直してる
• Code name “Roslyn”
• C#実装のC#コンパイラー
• コンパイラーの内部データを自由に使える
用途
• C#スクリプティング

• C#で設定ファイル書きたい(むしろXMLがいや)
• アプリの再起動なしでロジック更新

• ウェブ サービスで使う
• (GitHubとか想像してもらって)コード リポジトリの
インデックス化
• オンラインIDE、ビルド、テスト

• IDE連携
構文ハイライト
• C#は文脈キーワードだらけ
メソッド名
static IEnumerable<async> async()
{
var var = "var";
クラス名
var yield = new async();
yield return yield;
Func<string, Task<int>> async = async x =>
{
await Task.Delay(100);
変数
キーワード
return int.Parse(x);
};
var await = async(var).GetAwaiter().GetResult();
}
リアルタイム エラー検出
• エラー検出タイミングがビルド時とか遅い
• Visual Studioは常時やってる
コード補完
• タイピングめんどくさい
• 文法やライブラリ、いちいち覚えたくない
リファクタリング
• 最初からきれいなコード書くのめんどくさい
• でもほっときたくない
ということで
• 今、コンパイラーに求められる要件
• ソースコードのどこからどこまで(何行何列目)が
何かという情報がとれる
• 文脈に応じてソースコードを生成したり、書き替え
たりできる
• リアルタイム処理を必要とするので、パフォーマン
スも求められる
実はこれまで
• コンパイラー(パーサー)を2重開発してた
• コンパイル用
• IDE用
• まして、サード パーティ製コード解析プラグイン
も含めると、3重開発

• Java (EclipseとかIntelliJとか)でも
• 「Eclipseが対応するまでJava 8プレビュー試せない
や」
Clang
• Clangはその辺りをゴールの1つに掲げてる

• IDEで使う前提
• リファクタリングに使いやすいデータ構造の構文木
• インクリメンタル コンパイルとかもしやすい
IDE連携まで考えると
• 簡単そうに見える文法ですら、実装コストかな
り高い
• 2重開発はそれだけ大変
• だからIDEを信用しない/できない人も多い
• IDE対応待ってられない/待ちたくない

• “Roslyn”はそれを解消
• C#に新機能を足しやすくなる
• C# 6.0
C# 6.0 (予定)の例
Primary Constructor / Property Expressions
public class Point(int x, int y)
{
public int X => x;
immutableな型を作りやすく
public int Y => y;
}
Declaration Expressions

「式」で書けることの幅が広がる

while ((var line = stream.ReadLine()) != null)
line ...
if ((var x = obj as Point) != null)
x ...
以上。
まとめ
• .NET Frameworkの基礎機能
•
•
•
•

Garbage Collection
AppDomain
メタデータ、JIT
PCL

• C# 2.0~5.0… 6.0!
• ジェネリック、イテレーター
• LINQ、dynamic、async/await
• Compiler as a Service

More Related Content

What's hot

constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Genya Murakami
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
kikairoya
 

What's hot (20)

constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだconstexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
 
C#とILとネイティブと
C#とILとネイティブとC#とILとネイティブと
C#とILとネイティブと
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説
 
0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと0章 Linuxカーネルを読む前に最低限知っておくべきこと
0章 Linuxカーネルを読む前に最低限知っておくべきこと
 
非同期処理の基礎
非同期処理の基礎非同期処理の基礎
非同期処理の基礎
 
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
 
組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由組み込みでこそC++を使う10の理由
組み込みでこそC++を使う10の理由
 
名は体を表していますか
名は体を表していますか名は体を表していますか
名は体を表していますか
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
 
C#の強み、或いは何故PHPから乗り換えるのか
C#の強み、或いは何故PHPから乗り換えるのかC#の強み、或いは何故PHPから乗り換えるのか
C#の強み、或いは何故PHPから乗り換えるのか
 
C#で速度を極めるいろは
C#で速度を極めるいろはC#で速度を極めるいろは
C#で速度を極めるいろは
 
こわくない Git
こわくない Gitこわくない Git
こわくない Git
 
【Unite Tokyo 2019】Understanding C# Struct All Things
【Unite Tokyo 2019】Understanding C# Struct All Things【Unite Tokyo 2019】Understanding C# Struct All Things
【Unite Tokyo 2019】Understanding C# Struct All Things
 
プログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコードプログラムの処方箋~健康なコードと病んだコード
プログラムの処方箋~健康なコードと病んだコード
 
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
ちゃんとした C# プログラムを書けるようになる実践的な方法~ Visual Studio を使った 高品質・低コスト・保守性の高い開発
 
react-scriptsはwebpackで何をしているのか
react-scriptsはwebpackで何をしているのかreact-scriptsはwebpackで何をしているのか
react-scriptsはwebpackで何をしているのか
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
C# コーディングガイドライン 2013/02/26
C# コーディングガイドライン 2013/02/26C# コーディングガイドライン 2013/02/26
C# コーディングガイドライン 2013/02/26
 
プログラムを高速化する話
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話
 

Similar to C#や.NET Frameworkがやっていること

C++でぼくが忘れがちなこと
C++でぼくが忘れがちなことC++でぼくが忘れがちなこと
C++でぼくが忘れがちなこと
Toshihiko Ando
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門
伸男 伊藤
 

Similar to C#や.NET Frameworkがやっていること (20)

C#勉強会
C#勉強会C#勉強会
C#勉強会
 
C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)C# 8.0 Preview in Visual Studio 2019 (16.0)
C# 8.0 Preview in Visual Studio 2019 (16.0)
 
C++の復習
C++の復習C++の復習
C++の復習
 
T119_5年間の試行錯誤で進化したMVPVMパターン
T119_5年間の試行錯誤で進化したMVPVMパターンT119_5年間の試行錯誤で進化したMVPVMパターン
T119_5年間の試行錯誤で進化したMVPVMパターン
 
Objc lambda
Objc lambdaObjc lambda
Objc lambda
 
Net fringejp2016
Net fringejp2016Net fringejp2016
Net fringejp2016
 
Hello, C++ + JavaScript World! - Boost.勉強会 #11 東京
Hello, C++ + JavaScript World! - Boost.勉強会 #11 東京Hello, C++ + JavaScript World! - Boost.勉強会 #11 東京
Hello, C++ + JavaScript World! - Boost.勉強会 #11 東京
 
とあるFlashの自動生成
とあるFlashの自動生成とあるFlashの自動生成
とあるFlashの自動生成
 
C・C++用のコードカバレッジツールを自作してみた話
C・C++用のコードカバレッジツールを自作してみた話C・C++用のコードカバレッジツールを自作してみた話
C・C++用のコードカバレッジツールを自作してみた話
 
C++でぼくが忘れがちなこと
C++でぼくが忘れがちなことC++でぼくが忘れがちなこと
C++でぼくが忘れがちなこと
 
(ゲームじゃない方の)switchで遊びたい話
(ゲームじゃない方の)switchで遊びたい話(ゲームじゃない方の)switchで遊びたい話
(ゲームじゃない方の)switchで遊びたい話
 
Visual Studioで始めるTypeScript開発入門
Visual Studioで始めるTypeScript開発入門Visual Studioで始めるTypeScript開発入門
Visual Studioで始めるTypeScript開発入門
 
わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61わんくま同盟大阪勉強会#61
わんくま同盟大阪勉強会#61
 
Azure でサーバーレス、 Infrastructure as Code どうしてますか?
Azure でサーバーレス、 Infrastructure as Code どうしてますか?Azure でサーバーレス、 Infrastructure as Code どうしてますか?
Azure でサーバーレス、 Infrastructure as Code どうしてますか?
 
T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門T69 c++cli ネイティブライブラリラッピング入門
T69 c++cli ネイティブライブラリラッピング入門
 
C# と .NET と ・・・
C# と .NET と ・・・C# と .NET と ・・・
C# と .NET と ・・・
 
.NET Compiler Platform
.NET Compiler Platform.NET Compiler Platform
.NET Compiler Platform
 
EC-CUBEユーザカンファレンス2016
EC-CUBEユーザカンファレンス2016EC-CUBEユーザカンファレンス2016
EC-CUBEユーザカンファレンス2016
 
Howtoよいデザイン
HowtoよいデザインHowtoよいデザイン
Howtoよいデザイン
 
C++ tips2 インクリメント編
C++ tips2 インクリメント編C++ tips2 インクリメント編
C++ tips2 インクリメント編
 

More from 信之 岩永

Code Contracts in .NET 4
Code Contracts in .NET 4Code Contracts in .NET 4
Code Contracts in .NET 4
信之 岩永
 

More from 信之 岩永 (20)

YouTube ライブ配信するようになった話
YouTube ライブ配信するようになった話YouTube ライブ配信するようになった話
YouTube ライブ配信するようになった話
 
C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0C# 9.0 / .NET 5.0
C# 9.0 / .NET 5.0
 
C# コンパイラーの書き換え作業の話
C# コンパイラーの書き換え作業の話C# コンパイラーの書き換え作業の話
C# コンパイラーの書き換え作業の話
 
Unicode文字列処理
Unicode文字列処理Unicode文字列処理
Unicode文字列処理
 
C# 8.0 非同期ストリーム
C# 8.0 非同期ストリームC# 8.0 非同期ストリーム
C# 8.0 非同期ストリーム
 
C# 8.0 null許容参照型
C# 8.0 null許容参照型C# 8.0 null許容参照型
C# 8.0 null許容参照型
 
.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#.NET Core 2.x 時代の C#
.NET Core 2.x 時代の C#
 
C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1C# 7.2 with .NET Core 2.1
C# 7.2 with .NET Core 2.1
 
C#言語機能の作り方
C#言語機能の作り方C#言語機能の作り方
C#言語機能の作り方
 
Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6Unityで使える C# 6.0~と .NET 4.6
Unityで使える C# 6.0~と .NET 4.6
 
それっぽく、適当に
それっぽく、適当にそれっぽく、適当に
それっぽく、適当に
 
Modern .NET
Modern .NETModern .NET
Modern .NET
 
Deep Dive C# 6.0
Deep Dive C# 6.0Deep Dive C# 6.0
Deep Dive C# 6.0
 
Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3Orange Cube 自社フレームワーク 2015/3
Orange Cube 自社フレームワーク 2015/3
 
Code Contracts in .NET 4
Code Contracts in .NET 4Code Contracts in .NET 4
Code Contracts in .NET 4
 
今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略
 
今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略今から始める、Windows 10&新.NETへの移行戦略
今から始める、Windows 10&新.NETへの移行戦略
 
C# design note sep 2014
C# design note sep 2014C# design note sep 2014
C# design note sep 2014
 
.NET vNext
.NET vNext.NET vNext
.NET vNext
 
C#/.NETがやっていること 第二版
C#/.NETがやっていること 第二版C#/.NETがやっていること 第二版
C#/.NETがやっていること 第二版
 

Recently uploaded

研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
atsushi061452
 

Recently uploaded (14)

情報を表現するときのポイント
情報を表現するときのポイント情報を表現するときのポイント
情報を表現するときのポイント
 
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
 
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
 
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
 
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアルLoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
LoRaWAN無位置ロープ式水漏れセンサーWL03A 日本語マニュアル
 
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
 
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
 
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
 
Keywordmap overview material/CINC.co.ltd
Keywordmap overview material/CINC.co.ltdKeywordmap overview material/CINC.co.ltd
Keywordmap overview material/CINC.co.ltd
 
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイルLoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
 
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdfネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
 
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
 
Intranet Development v1.0 (TSG LIVE! 12 LT )
Intranet Development v1.0 (TSG LIVE! 12 LT )Intranet Development v1.0 (TSG LIVE! 12 LT )
Intranet Development v1.0 (TSG LIVE! 12 LT )
 
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
 

C#や.NET Frameworkがやっていること