8. メモリ管理(1)
• スタック
• 一番上に積む・一番上から取り出す
• ローカル変数用
• 管理は楽
• スコープがはっきりしてないと使えない
int x = 1;
int y = 2;
int z = 4;
int sum = x + y + z; 1 1
2
load 1 load 2
3
add
3
4
load 4
7
add
27. unsafeでも制限付き
• class (参照型)はアドレス取れない
• 仮想メソッド テーブル(vtable)とか入ってるし
• 親クラス側のレイアウト変更の影響受けるし
class Class
{
public int x;
public int y;
}
var c = new Class();
fixed (void* p = &c) { } // error
fixed (void* p = &c.x) { } // OK
fixed (void* p = &c.y) { } // OK
ピン止め必須
• 値型のメンバーのアドレスは取れる
• オブジェクト自体のアドレスは取れ
ない
28. unsafeでも制限付き
• struct (値型)はアドレス取れる
• ただし、メンバーが全部値型の時のみ†
• ↑「unmanaged型」と呼ぶ
struct UnmanagedStruct
{
public int x;
public int y;
}
var u = new UnmanagedStruct();
void* p1 = &u; // OK
void* p2 = &u.x; // OK
void* p3 = &u.y; // OK
メンバーが
全部値型
無制限にアドレス取れる
† 正確には、再帰的に全子要素が値型、かつ、型パラメーターも値型
29. unsafeでも制限付き
• struct (値型)であっても制限付き
• メンバーに1つでも参照型を含むとダメ
• ↑「managed型」と呼ぶ
struct ManagedStruct
{
public int x;
public string y;
}
var u = new ManagedStruct();
void* p1 = &u; // error
void* p2 = &u.x; // OK
void* p3 = &u.y; // error
メンバーに
参照型が1つ
本体のアドレス取れない
値型のメンバーのところ
だけはアドレス取れる
64. メタデータとは
• 実行可能ファイル中には本来不要なデータ
• プログラムを作るためのデータ
• 実行に必要なデータよりもメタ(高次)
var x = p.X;
var y = p.Y;
var z = p.Z;
var pp = (byte*)&p;
var x = *((int*)pp);
var y = *((int*)(pp + 4));
var z = *((int*)(pp + 8));
プロパティ名とか
単にプログラムを動かすだけなら
相対アドレスだけわかればいい
.NETでは、こういうメタデータを実行可能ファイルに残す
72. 差分ダウンロード
※ Windowsストア アプリはこういう仕組み持ってる
アプリA ライブラリX
ライブラリY
アプリAパッケージ version 1
version 1
アプリA ライブラリX
ライブラリY
アプリAパッケージ version 2
version 2
version 1 version 1
version 1 version 1
ライブラリX
差分
version 2
ダウンロード
アプリA ver.1
インストール機
バージョンアップ時
更新
ここだけ新しい
82. メモリ レイアウト
• この4とか8の意味
public struct Point
{
public int X;
public int Y;
public int Z;
}
Point
X
Y
Z
4バイト
8バイト
※レイアウトがどうなるかは環境依存
83. メモリ レイアウト
• この4とか8の意味
public struct Point
{
public int X;
public int Y;
public int Z;
}
Point
X
Y
Z
4バイト
8バイト
※レイアウトがどうなるかは環境依存
ILの時点までは名前
で参照してる
ネイティブ コードは
レイアウトを見て
数値で参照してる
84. 数値でのフィールド参照
• 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)
130. 型違いの処理
• 型だけ違う処理、コピペで書いていませんか
int Max(int x, int y)
{
return x > y ? x : y;
}
class IntStack
{
void Push(int item)
{ … }
int Pop()
{ … }
}
double Max(double x, double y)
{
return x > y ? x : y;
}
class DoubleStack
{
void Push(double item)
{ … }
double Pop()
{ … }
}
131. ジェネリック†
• 型をパラメーター化
† generics。MS翻訳ルール的に、語尾 s は取るんですって
複数形のs扱いで。単複の区別のない言語に訳すとき
class Stack<T>
{
void Push(T item)
{ … }
T Pop()
{ … }
}
T Max<T>(T x, T y)
where T : IComparable<T>
{
return x.CompareTo(y) > 0 ? x : y;
}
int Max(int x, int y)
{
return x > y ? x : y;
}
class IntStack
{
void Push(int item)
{ … }
int Pop()
{ … }
}
138. いろいろ窮屈な面も
• .NETのジェネリックでは、インターフェイス制
約かけないとメソッドすら呼べない
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
144. データの列挙の例
• substringの列挙
• データを作る側と使う側を分けないなら
static void WriteSubstrings(string s)
{
for (var len = s.Length; len >= 1; len--)
for (var i = 0; i <= s.Length - len; i++)
Console.WriteLine(s.Substring(i, len));
}
いつもConsole.Writeしたいわけじゃない
substringを使いたいたびに同じコード書くの?
145. データの列挙の例
• substringの列挙、分けたいなら
• ↓こんな感じのクラスを書けばいいんだけど…
class GetSubstringEnumerator
{
public string Current { get; private set; }
string _s; int _len; int _i;
public GetSubstringEnumerator(string s)
{
_s = s; _len = s.Length; _i = 0;
}
public bool MoveNext()
{
for (; _len >= 1; _len--, _i = 0)
for (; _i <= _s.Length - _len; )
{
Current = _s.Substring(_i, _len);
_i++;
return true;
}
return false;
}
}
こういうクラスを
イテレーター(iterator)とか
列挙子(enumerator)って言う
作るの結構面倒
146. イテレーターの例
• 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);
}
foreach (var x in GetSubstrings("abcd"))
Console.WriteLine(x);
実装側
使う側
イテレーター ブロック†
(= yield returnを持つ関数ブロック)
イテレーター クラスを自動生成
147. 内部実装(全体像)
• クラス生成
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; }
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);
}
148. 内部実装(ローカル変数)
• ローカル変数 → フィールド
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;
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);
}
149. 内部実装(yield return)
• yield return → 状態記録、return、caseラベル
• 中断と再開
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;
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);
}
150. 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;
}
内部実装(yield return)
• 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以外の
場所はほぼ同じ
yield return x;
_state = 1;
Current = x;
return true;
case 1:;
† forループ内にラベル張れないからさっきみたいな複雑なコードになるけども
switchで囲う
164. ローカル変数のキャプチャ
• 実はクラス生成
IEnumerable<int> Multiply(IEnumerable<int> input)
{
var a = int.Parse(Console.ReadLine());
return input.Select(x => a * x);
}
IEnumerable<int> Multiply(IEnumerable<int> input)
{
var _ = new Anonymous();
_.a = int.Parse(Console.ReadLine());
return input.Select(_.X);
}
class Anonymous
{
public int a;
public int X(int x)
{
return a * x;
}
}
ローカル変数が
フィールドに昇格
165. 匿名型
• 1か所でしか使わないような型は作らなくてい
い
• immutableなクラスを自動生成
• GetHashCode、等値比較、ToStringを完備
source.GroupBy(p => new { p.X, p.Y });
(XとYでグループ化)
class Anonymous
{
public int X { get; private set; }
public int Y { get; private set; }
public Anonymous(int x, int y) { X = x; Y = y; }
}
167. 語順を変えるだけで
• 語順のインパクト意外と大きい
var result = data
.Where(x => x < 3)
.Select(x => x * x)
.GroupBy(x => x % 2)
.Select(g => g.Sum());
var result1 =
Select(
GroupBy(
Select(
Where(
data,
x => x < 3),
x => x * x),
x => x % 2),
g => Sum(g));
逆順 処理順
( ) が遠い
( ) が近い
普通の静的メソッド 拡張メソッド
168. 静的メソッド
• “static”と言ってもいろいろ
static bool globalFlag;
いつどこで誰が書き換えるか
わからないのがダメ
静的フィールド(書き換え可能)
const int Max = 100;
static readonly TimeSpan interval = TimeSpan.FromSeconds(1);
定数 or 読み取り専用静的フィールド
書き換え禁止すれば無害
static int Clip(int x) { return Math.Max(x, Max); }
静的メソッド
無害なものにしか触れない限り、無害
173. ダック タイピング†
• 同じ名前のメンバーを持っていれば
同じ型扱いできないか
† アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ」
という論法からきた比喩表現
class Point
{
public int X { get; set; }
public int Y { get; set; }
public void Add(Point p)
{
X += p.X;
Y += p.Y;
}
}
174. ダック タイピング†
• 同じ名前のメンバーを持っていれば
同じ型扱いできないか
† アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ」
という論法からきた比喩表現
class Point
{
public int X { get; set; }
public int Y { get; set; }
public void Add(Point p)
{
X += p.X;
Y += p.Y;
}
}
?
XとYというプロパティ
(またはフィールド)を
持っている任意の型を
使いたい
こういう処理にはリフレクション
(メタデータの実行時利用)が必要
175. dynamic型
• ダック タイピング用の型
class Point
{
public int X { get; set; }
public int Y { get; set; }
public void Add(dynamic p)
{
X += p.X;
Y += p.Y;
}
}
XとYというプロパティ
(またはフィールド)を
持っている任意の型を
使える
もちろん、内部的には
リフレクション使ってる
192. 同期処理
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;
194. 非同期処理(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演算子が増えただけ
• ダイアログの数が増えても平気
214. C# 6.0 (予定)の例
public class Point(int x, int y)
{
public int X { get } = x;
public int Y { get } = y;
}
Primary Constructor / Property Expressions
while ((var line = stream.ReadLine()) != null)
line ...
if ((var x = obj as Point) != null)
x ...
Declaration Expressions
immutableな型を作りやすく
「式」で書けることの幅が広がる