タグ:C# ( 5 ) タグの人気記事
「C# エッセンシャルズ」まとめ その4

2.13 デリゲート (p.70, 71)

 メソッドを関数オブジェクトっぽいものに変換する機能。名前とメソッドシグネチャを含めて宣言を行う。new の引数にメソッドを渡してインスタンスを作成する。
delegate bool Filter(string s);

class Test {
static void Main() {
Filter f = new Filter(FirstHalfOfAlphabet);
Display(new String[] {"Ant", "Lion", "Yak"}, f);
}

static bool FirstHalfOfAlphabet(string s) {
return "N".CompareTo(s) > 0;
}

static void Display(string[] names, Filter f) {
int count = 0;
foreach(string s in names) {
if (f(s)) // デリゲートの呼び出し
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
}

 デリゲートの += メソッドを使って、複数のメソッドの保存と呼び出しを行うことができる。
using System;
delegate void MethodInvoker();
class Test {
static void Main() {
new Test(); //=> "Foo", "Goo"
}
Test () {
MethodInvoker m = null;
m += new MethodInvoker(Foo);
m += new MethodInvoker(Goo);
}
void Foo() {
Console.WriteLine("Foo");
}
void Goo() {
Console.WriteLine("Goo");
}
}


以下、「C#エッセンシャルズ」には無い話。

C# 2.0 からは メソッドからデリゲートへの暗黙の変換が可能になったらしい。上の例は、こう書ける。
delegate bool Filter(string s);

class Test {
static void Main() {
Filter f = FirstHalfOfAlphabet; // 暗黙に変換
Display(new String[] {"Ant", "Lion", "Yak"}, f);
}

static bool FirstHalfOfAlphabet(string s) {
return "N".CompareTo(s) > 0;
}

static void Display(string[] names, Filter f) {
int count = 0;
foreach(string s in names) {
if (f(s)) // デリゲートの呼び出し
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
}

匿名メソッド
 C# 2.0 から匿名メソッドというものが導入された、、、が、もっと便利な構文 (ラムダ式) が C# 3.0 から導入されたようなので、省略。

ラムダ式
基本形 (式形式)
(input parameters) => expression

 パラメータの括弧は、パラメータの数が1つの場合は省略可能。パラメータの型は、コンパイラによる型推論が可能な場合は省略可能。
x => x > 0;
(x, y) => return x + y;
(int x, string s) => s.Length > x

本体部分が複数のステートメントからなる場合は、中括弧でくくる (ステートメント形式)。
n => {
string s = n + " " + "World";
Console.WriteLine(s);
}

標準ジェネリックデリゲート
基本形
Func<TParam1, TParam2, ..., TResult>

TParam1やTParam2 に引数の型を、Tresult に返り値の型を指定する。これを使って、最初の例を書き直してみると、
class Test {
static void Main() {
Display(new String[] {"Ant", "Lion", "Yak"},
s => "N".CompareTo(s) > 0); // ここがラムダ式
}

// 標準ジェネリックデリゲートでラムダ式を受け取る
static void Display(string[] names, Func f) {
int count = 0;
foreach(string s in names) {
if (f(s)) // デリゲートの呼び出し
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
}
最初の例と比べると、かなり簡潔に書けるようになった。

 Java陣営が、クロージャを導入するとかしないとか言っている間に (結局、JDK7 には入らないみたいだけど) C# の関数型機能がこんなに充実しているのに驚き。

参考:
デリゲート (C# によるプログラミング入門)
関数型言語由来の新機能 (C# によるプログラミング入門)
MSDN: Func(T, TResult) デリゲート (System)
[PR]
by fkmn | 2009-04-13 23:55 | IT
「C# エッセンシャルズ」まとめ その3

2.9.7 メソッド (p.51, 52)

・引数の値渡し/参照渡し
 デフォルトでは、C# の引数は値渡しされる。ref 修飾子を仮引数にしていすると、参照渡しになる。

static void Foo(int p) { ++p; }      // 値渡し
static void Bar(ref int p ) { ++p; } // 参照渡し
public static void Main() {
int x = 8;
Foo(x);
Console.WriteLine(x); //=> 8
Bar(x);
Console.WriteLine(x); //=> 9
}
 デフォルトが値渡しってのは、意外と珍しいかも。

・out修飾子
 メソッドから値を受け取るための引数を指定するための修飾子。メソッド内で変数に値を割り当てられる事が保証されるので、メソッド呼び出しの時点では初期化を行わなくても良い。
using System;
class Test {
static void Split(string name, out string firstNames,
out string lastName) {
int i = name.LastIndexOf(' ');
firstNames = name.Substring(0, i);
lastName = name.Substring(i + 1);
}

public static void Main() {
string a, b; // 初期化不要
Split("Nuno Bettencourt", out a, out b); // 呼び出し側でも out が必要
Console.WriteLine("FirstName: {0}, LastName: {1}", a, b);
//=> FirstName: Nuno, LastName: Bettencourt
}
}

 うーん、これは正直どうなんだろ。引数で結果を受け取るという機会が、Cならまだしも、C#ではほとんどないような気がするんだけど。少なくとも、僕は使わないだろうな。

・params修飾子
 params修飾子は、メソッドの最後のパラメータに指定することができる。これが指定されると、メソッドは、特定の方のパラメータをいくつでも受け取ることができる。
using System;
class Test {
static int Add(params int[] iarr) {
int sum = 0;
foreach (int i in iarr)
sum += i;
return sum;
}

static void Main() {
int i = Add(1, 2, 3, 4);
Console.WriteLine(i); //=> 10
}
}

 ruby の * が付いた仮引数みたいなものだね。ちなみに、Ruby 1.9 からは、* は最後以外の引数にも指定できるようになりました (って、いつの間にか ruby の話になってる・・・w)。
def add(*iarr, name)
sum = 0
sum = iarr.inject { |r, s| r += s }
return "sum: #{sum}, name: #{name}"
end

def main
puts add(1, 2, 3, 4, 'fkmn') #=> "sum: 10, name: fkmn
end

main()


2.9.9 インスタンスコンストラクタ (p.56, 57)

 クラス/構造体では、コンストラクタをオーバーロードする事が可能。thisキーワードを使うと、オーバーロードされたコンストラクタを呼び出すことができる。baseキーワードを使うと、親クラスのコンストラクタを呼び出すことができる。
class B {
public int x;
putlic B(int a) {
x = 1;
}
public B(int a, int b) {
x = a * b;
}
// クラスBのコンストラクタは、すべてパラメータを受け取る
}

class D : B {
public D() : this(7) {} // オーバーロードされたコンストラクタを呼び出す
public D(int a) : base(a) {} // 親クラスのコンストラクタを呼び出す
}

 構文にちょっと違和感を感じるけど、機能自体はまぁ普通、というか無いと困る。

2.10.5 明示的なインターフェイスの実装 (p.65)

 インターフェイスメンバとそのクラスまたは構造体の既存のメンバの名前が重複した場合は、競合を解決するために、インターフェイスのメンバを明示的に実装することができる。

public interface IDelete {
void Delete();
}

public interface IDesignTimeControl {
object Delete();
}

public class TextBox : IDelete, IDesignTimeControl {
void IDelete.Delete() { ... }
object IDesignTimeControl.Delete() { ... }
// 競合を解決するためには、片方だけを明示的に実装すれば十分
}

[PR]
by fkmn | 2009-04-10 23:55 | IT
「C# エッセンシャルズ」まとめ その2

2.9.5 プロパティ (p.47, 48)

 アクセサ (Java でいう getter や setter) を生成する機能。

public class Well {
decimal dollars; // private フィールド
public int Cents {
get { return(int)(dollars * 100); }
set {
if (value >= 0)
dollars = (decimal)value / 100;
}
}
}

class Test {
static void Main() {
Well w = new Well();
w.Cents = 25; // set
int x = w.Cents; // get
w.Cents += 10; // get と set
}
}
 get{} だけ定義すると読み取り専用、set{} だけ定義すると書き込み専用というように定義できる。

 プロパティは、MSIL (Microsoft Intermediate Language) の段階で、get_XXX や set_XXX に変換される、とのこと。
public int get_Cents {...}
public void set_Cents (int value) {...}

 したがって、上の例のように Cents というプロパティを定義しているクラスでは、(C# の段階で) get_Cents や set_Cents といったメソッドは作成できない (Visual C# 2008 Express Edition で確認)。
public class Well {
decimal dollars; // private フィールド
public int Cents {
get { return (int)(dollars * 100); }
set {
if (value >= 0)
dollars = (decimal)value / 100;
}
}

// ***** エラー!! *****
public int get_Cents() {
return (int)dollars;
}
}


2.9.6 インデクサ (p.48, 49)

 配列スタイルの [] 構文をつかって、オブジェクトにアクセサを生やす。コレクションをカプセル化するクラスの生成等に使える。
public class ScoreList {
int[] scores = new int[5];

// インデクサ
public int this[int index] {
get { return scores[index]; }
set {
if (value >= 0 && value <= 10)
scores[index] = value;
}
}

// プロパティ (読み取り専用)
public int Average {
get {
int sum = 0;
foreach (int score in scores)
sum += score;
return sum / scores.Length;
}
}
}

class IndexerTest {
static void Main() {
ScoreList sl = new ScoreList();
sl[0] = 9;
sl[1] = 8;
sl[2] = 7;
sl[3] = sl[4] = sl[1];
System.Console.WriteLine(sl.Average); //=> 8
}
}

 微妙に分かりにくい機能。obj[0] を obj.0 というプロパティに対する構文糖衣と考えると、少し分かりやすいかも。


次回へ続く。
[PR]
by fkmn | 2009-04-08 23:55 | IT
「C# エッセンシャルズ」まとめ その1
 「C#エッセンシャルズ」について、というよりも、C# そのものの言語仕様について、個人的に「おっ」と思った部分についてのまとめメモ。このペースでいくと、全3-4回ぐらいになりそう。

verbatim文字列リテラル (p.15)

 Stringリテラルの先頭に @ をつけると「verbatim文字列リテラル」として取り扱われる。\ などのエスケープ文字が含まれていても、そのままの内容として扱われる。

public void StringDemo() {
string a1 = "\\\\server\\fileshare\\helloworld.cs";
string a2 = @"\\server\fileshare\helloworld.cs";
Console.WriteLine(a1 == a2); //=> True

string b1 = "First Line\r\nSecond Line";
string b2 = "First Line
Second Line";
Console.WriteLine(b1 == b2); //=> True
}

 Perl や Ruby のシングルクォートで囲まれた文字列みたいなものか。


using ステートメント (p.32)

 ruby の ブロックを使った open みたいなことが C# でもできる。
using (FileStream fs = new FileStream(fileName, FileMode.Open)) {
// fs に対する処理
}

 usingステートメントを使うと、IDisposableインターフェースを実装する変数の Dispose メソッドが自動で呼び出される。
 上のコードは、次のコードと全く同じとの事。
FileStream fs = new FileStream(fileName, FileMode.Open);
try {
// fs に対する処理
}
finally {
if (fs != null)
((IDisopsable)fs).Dispose();
}


2.7.3 仮想関数メンバ (p.37, 38)

 オーバーライドされる関数メンバは「virtual」キーワードで宣言する。オーバーライドする側は「override」キーワードを指定する。
class Location {
// オーバーライドされる関数メンバ
public virtual void Display() {
Console.WriteLine(Name);
}
}

class URL : Location {
// Location クラスの Display メソッドをオーバーライド
public override void Display() {
// 先頭の http:// を切り捨て
Console.WriteLine(Name.Substring(7));
}
}



2.7.6 継承したメンバを隠す機能 (p.39, 40)

 new キーワードの、コンストラクタ呼び出し以外の機能。
using System;
class B {
public virtual void Foo() {
Console.WriteLine("In B.");
}
}

class D : B {
public override void Foo() {
Console.WriteLine("In D.");
}
}

class N : D {
// Dクラスの Fooメソッドを隠す
public new void Foo() {
Console.WriteLine("In N.");
}
}

class Test {
public static void Main() {
N n = new N();
n.Foo(); //=> "In N."
((D)n).Foo(); //=> "In D."
((B)n).Foo(); //=> "In D."
}
}

 ちょっと、使いどころがまだよくわからないな。


2.9.3 フィールド、2.9.4 定数 (p.45, 46)

 フィールドに readonly 修飾子をつけると、読み取り専用フィールドとなり、値を割り当てた後にそれを修正する事ができなくなる。「readonly」は実行時に評価される
class MyClass {
int x;
float y = 1, z = 2;
static readonly int MaxSize = 10;
}


 「const」で定数を宣言する。コンパイル時に評価され、暗黙的に静的になる。コンパイル時に評価されるので、コンパイラによる最適化が行われる。
public static double Circumference(double radius) {
return 2 * Math.PI * radius;
}
は、最適化されて、次のように評価される。
public static double Circumference(double radius) {
return 6.28318530717959 * radius;
}


次回へ続く。
[PR]
by fkmn | 2009-04-06 23:55 | IT
C# 始めました
新しいプロジェクトで 、僕が今まで一行も書いた事が無い C# を使う事になったとさ。
<del>Java+Perl/Linux な Webアプリケーションのすぐ後で、 C# な Windows アプリケーションを作る事になった僕の専門性はいずこへ・・・? </del>

ということで、言語仕様をよく理解しないうちに開発を始めるわけにも行かないので、とりあえず手当たり次第に本を買ってみた。

a0057891_0244686.jpg

冷静に考えてみると、ちと買いすぎた気がしないでもない・・・。しかも、C# に関係ない本が混ざったりしてるのは、気のせいだよな、うん。

とりあえず「C#エッセンシャルズ」は読み終わったので、明日あたりまとめる予定。
[PR]
by fkmn | 2009-04-04 23:55 | IT


とあるWebアプリケーションエンジニアの日記
S M T W T F S
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
カテゴリ
以前の記事
ブログパーツ
リンク
検索
タグ
最新のトラックバック
プログラミングが「出来る..
from とりあえず9JP?
Genographic ..
from ナンジャモンジャ
ジュセリーノ
from ありの出来事
くちこみブログ集(ライフ..
from くちこみブログ集(ライフ)(..
以降、丁寧語で行こう!
from エッセイ的な何か
その他のジャンル
ファン
記事ランキング
ブログジャンル
画像一覧

fkmnの最近読んだ本 フィードメーター - フッ君の日常 あわせて読みたい AX