Experiments Never Fail

Objective-C と Java と C# でクロージャ的な書き方の比較

クロージャとかラムダとか匿名** とか名前はいろいろですけど、各言語の書き方と動き(特に変数の扱い)について比べてみました。

ついでに非同期処理の例にもなってしまいました。

Objective-C #

Blocks を使います。

int x = 1;
__block int y = 2;

dispatch_queue_t q_global = dispatch_get_global_queue(0, 0);
dispatch_async(q_global, ^{
x = 10; // できない(コンパイルエラー
y = 20; // できる

int z = x + y;

[self dispValue:z]; // self の参照カウンタが+1される
});

普通に宣言した変数を Block の中で使うと、自動的に「キャプチャ」され、変数の複製される。この変数には、 Block 内では代入できずコンパイルエラーとなる。
__block を付けた変数は、Block 内外で同じ実体を参照でき、代入もできる。
self やプロパティを Block 内で使用すると参照カウンタがインクリメントされ、明示的に release しないとリークする。
あるいは、Block 外で __weak を付けた変数に代入しておくと、これは参照カウンタがインクリメントされない。

Java 6 (Android ベースなので…) #

匿名クラスです。

int x = 1;
final int y = 2;

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new Runnable() {
@Override
public void run() {
y = 20; // できない(コンパイルエラー
int z = x + y; // できない(コンパイルエラー

String typeName = this.getClass().getInterfaces()[0].getName(); // Runnable になる
}
});

Java は匿名クラスの実装中に使える変数はかなり制限がある。
普通に宣言した変数は、匿名クラス内では使えない(コンパイルエラー)。
final を付けて宣言した変数は、匿名クラス内では参照のみ可能。ちょうど Objective-C の通常変数を Block 内で使った時と同じ。
Obj-C の self にあたる this は匿名クラス内では、その匿名クラスを示す。

C# #

ラムダ式です。

int x = 1;
const int y = 2;
Task.Factory.StartNew(() =>
{
x = 10; // OK
y = 20; // これはダメ、const だから。
var z = x + y;

var typeName = this.GetType().Name;
});

C# はかなりゆる〜い印象。
普通に宣言した変数を、ラムダ式の中でも自由に read/write できてしまう。write できちゃうのはこわい。
this は、ラムダ式の外側のクラスを示す。

所感 #

個人的には、Java のガチガチなのが好きかも。できる事が限定されているのでミスしにくい。
Objective-C は、ローカル変数は良いけど、self とか使っちゃうミス起こしそう。
C# は、普通に書き換えられて超不安、という感じ。なるべく const 使うようにしたい。

しかし記述量は C# が一番少ないですね、Xamarin いいよ Xamarin。。。

published at tags: Java Xamarin C# Objective-C