.NET Framework の歴史
いつ、どのような機能が追加されたのか、わけがわからなくなってきたので、整理。
.NET Framework Version: 1.0
開発環境 Visual Studio 2002
- Code Name: Rainier
- 内部バージョン: 7.0
C# Version 1.0
.NET Framework Version: 1.1
開発環境 Visual Studio 2003
- Code Name: Everett
- 内部バージョン: 7.1
- 機能拡張
- .NET Compact Framework 1.0搭載
- IntelliSense の機能向上
- デバッガの機能向上(コレクションクラスのデータ表示)
- コンパイル方法や構築方法のカスタマイズ
- J# 対応
- ASP .NET Mobille Controls
C# Version 1.2
.NET Framework Version: 2.0
開発環境 Visual Studio 2005
- Code Name: Whidbey
- 内部バージョン: 8.0
C# 2.0
- Generics
- iterator
- partial class
- Nullable 型
- 匿名メソッド
- 名前空間のエイリアス修飾子
- 静的クラスの変更
- 外部アセンブリのエイリアス
- プロパティ アクセサのアクセシビリティ
- デリゲートの共変性と反変性
- デリゲートの宣言の簡素化
- 固定サイズ バッファ
- フレンド アセンブリ
- #pragma 警告
- volatile
- C# コンパイラ オプションの変更
.NET Framework Version: 3.0
背景
- Ajaxなどのユーザーエクスペリエンスの向上
- 相互接続性、セキュリティの向上
- Vistaに標準で .NET Framework 3.0が搭載された。
- Windows XP, Windows Server 2003
(Service Pack 1 以上)もサポート
- 開発環境は Visual Studio 2005 のままで、.NET Framework
3.0を搭載することにより、Vista上でのWPFなどの新機能を実装できるようになった。
- Vista US版 RTM 2006/11/8。
- Vista 日本語版 発売開始は、2007/1/30
リリース日
.NET Framework 3.0の構成
- .NET Framework 2.0
- WPF(Windows Presentation Foundation)
- WCF(Windows Communication Foundation)
- WF(Windows Workflow Foundation)
- WCS(Windows CardSpace)
開発環境 Visual Studio 2005
- Code Name: Whidbey
- 内部バージョン: 8.0
.NET Framework Version: 3.5
開発環境 Visual Studio 2008
- Code Name: Orcas
- 内部バージョン: 9.0
- 機能拡張
- .NET Framework の複数バージョンをサポート
- LINQ
- Office アプリケーション対応(VSTO, Office 2007のリボンなど)
- Professionalに単体テスト機能
- Expression Web, Expression Blend 対応
- Windows Vista 用の魅力的なアプリケーションの構築 (WPF)
- WEB対応強化(AJAX, JSON対応。WCFによるRSS, REST対応)
- WCF
- CSSサポートの強化
- 全体的なパフォーマンスと安定性
- BigInteger Class
- HashSet Class
- System.IO.Pipes 名前空間 (匿名パイプと名前付きパイプ)
C# 3.0
C# 2.0の主な追加機能
Generics
.Net Framework 2.0から、System.Collections.Generic という新しい名前空間が追加されました。
.NET Framework 1.0 の
System.Collections.ArrayList では、Object
にいろいろな値を格納することができた。しかし、次のような問題がありました。
これに対してGenericsは、コンパイル時にタイプセーフなコレクションを作成できます。ArrayList
と List <T> は似ていますが、Tが値型の場合 List <T>のほうが高速です。
|
System.Collections |
System.Collections.Generics |
リスト関連 |
ArrayList |
List<T> |
|
LinkedList(T) |
SortedList |
SortedList(TKey, TValue) |
辞書 |
DictionaryBase |
Dictionary(TKey, TValue) |
|
SortedDictionary(TKey, TValue) |
ハッシュ |
Hashtable |
HashSet(T) |
キュー |
Queue |
Queue(T) |
スタック |
Stack |
Stack(T) |
例
private
static
void
ListForeach()
{
List<int>
list = new
List<int>();
list.Add(0);
list.Add(1);
list.Add(2);
foreach (int
i in
list)
Console.WriteLine(i.ToString());
Console.ReadLine();
}
匿名メソッド
C# 1.0, 1.2
までは、次のようにデリゲートの宣言と本体を別々に定義する必要がありました。
class
Program
{
static
void
Main(string[]
args)
{
System.Threading.ThreadStart
deleg = new
System.Threading.ThreadStart(myFunc);
System.Threading.Thread
t1 = new
System.Threading.Thread(deleg);
t1.Start();
Console.ReadLine();
}
static
void
myFunc()
{
Console.WriteLine("Hello:
C# 1.0");
}
}
}
C# 2.0 の匿名メソッドの導入により、次のように簡単に書けるようになります。
class
Program
{
static
void
Main(string[]
args)
{
System.Threading.Thread
t2 = new
System.Threading.Thread(
delegate()
{ Console.WriteLine("Hello:
C# 2.0");});
t2.Start();
Console.ReadLine();
}
}
iterator
従来の .NET 1.0, 1.1 の例
従来、foreach で扱えるようなコレクションを作成するためには、IEnumerable,
IEnumerator を実装する必要がありました。たとえば、System.DateTime 構造体を foreach
で回すことはないと思いますが、年、月、日を foreach で取り出すコードを実装すると次のようになります。
using
System;
using System.Collections;
public
class
MyItem
{
private
Object
_obj;
public MyItem(Object
obj)
{
this._obj
= obj;
}
public
override
string
ToString()
{
return
(_obj.GetType().ToString() +
":" + _obj.ToString());
}
}
public
class
MyList
: IEnumerable
{
public MyList(DateTime
x)
{
n = x;
}
System.DateTime
n;
public
IEnumerator
GetEnumerator()
{
return
new
IMyEnumerator(n);
}
}
public
class
IMyEnumerator
: IEnumerator
{
public
int[]
myItem = new
int[3];
int position
= -1;
public
IMyEnumerator(DateTime
x)
{
myItem[0] = x.Year;
myItem[1] = x.Month;
myItem[2] = x.Day;
}
public
bool
MoveNext()
{
position++;
return
(position < myItem.Length);
}
public
void
Reset()
{
position = -1;
}
public
object
Current
{
get
{
try
{
return
myItem[position];
}
catch
(IndexOutOfRangeException)
{
throw
new
InvalidOperationException();
}
}
}
}
class
App
{
static
void Main()
{
MyList
myList = new
MyList(DateTime.Now);
foreach
(int
myItem in
myList)
Console.WriteLine(myItem.ToString());
Console.ReadLine();
}
}
C# 2.0 では
C# 2.0 では、yield return, yield break という
iterator が導入され、同じことが次のように非常に簡潔に書けるようになりました。
using
System;
public
class
MyList :
System.Collections.IEnumerable
{
public MyList(DateTime x)
{
n = x;
}
System.DateTime n;
public System.Collections.IEnumerator
GetEnumerator()
{
yield return
n.Year;
yield return
n.Month;
yield return
n.Day;
}
}
class
App
{
static void Main()
{
MyList myList = new
MyList(DateTime.Now);
foreach (int
myItem in myList)
Console.WriteLine(myItem.ToString());
Console.ReadLine();
}
}
partial class
Form1.cs
と、Form1.Designer.cs で自動生成されるコードと開発者が実装するコードが partical class
により分離されている。
Form1.cs の例
namespace
WindowsFormsApplication1
{
public
partial
class
Form1
: Form
{
public Form1()
{
InitializeComponent();
}
}
}
Form1.Designer.cs の例
namespace
WindowsFormsApplication1
{
partial
class
Form1
{
///
Windows フォーム デザイナで生成されたコード
>
}
}
Nullable 型
Nullable 型 (null 許容型)とは
最近、個人情報の扱いが厳しくなっていますが、データベースでは、NULL
を許容する宣言が可能です。たとえば、個人情報のテーブルを作成し、電話番号の入力は任意とし、電話番号は NULL
を許容する設定にすることができます。電話番号であれば、String クラスを使うことになると思いますが、この場合 class
は参照型なので、null
を扱うことができます。一方、生年月日を扱う場合、これも個人情報なので入力は任意としたいという要求があったとしましょう。.NET
Framework では、DateTime 型は構造体なので値型です。したがって、null
を扱うことができません。このような値型に対して、Null を許容するために導入されたのが Nullable 型です。
例
構造体(値型)の DateTime 変数に null を代入しようとすると、「エラー 1 Null 非許容の値型であるため、null を 'System.DateTime'
に変換できません。」 というエラーになる。
DateTime dt =
null;
// エラー
null 許容型を使うことにより、値型でも null
を扱うことができるようになる。
DateTime? dt =
null; //
null を扱える。
例
int? x = null;
これは、次の省略形です。
Nullable<int> x = null;
メンバーに null が来る可能性があるため、次のメンバーが用意されています。
メンバー |
説明 |
HasValue |
現在の Nullable オブジェクトに値があるかどうか。 |
Value |
現在の Nullable 値の値を取得します。 |
例
int? n =
null;
if (n.HasValue)
Console.WriteLine(n.Value);
else
Console.WriteLine("null です。");
null 許容型を使う場合の注意
NULL を許容するということは、データが NULL
であった場合の処理が必要になります。このとき、単に データを参照するだけならいいのですが、論理式の変数として NULL
が入り込むと厄介なことになります。
NULL の弊害に関しては、NULL撲滅委員会
に詳しくまとまっていますので、ご一読されるとよいでしょう。個人的には、NULL を使用する変数が参照されるだけなら、Nullable
型を使用したほうがコードが見やすくなり、良いと思います。一方、NULL を使用する変数を元に、計算を行う必要がある場合には、極力
NULL を避けるべきです。特に、NULL を許容する論理計算は最悪です。
次の論理計算を実行した場合、
bool?[]
bxarray = { true,
false,
null };
bool?[] byarray = { true,
false,
null };
foreach (bool?
y in
byarray)
foreach (bool?
x in
bxarray)
{
WriteVal(x, y, x | y); // 結果を出力
WriteVal(x, y, x & y); // 結果を出力
}
次のような結果になります。null
が入った場合の論理和、論理積の結果で、赤でマークした部分に注目してください。このような
3値論理計算を間違いなくこなせますか?安易に Null を許容するのはやめたほうがいいでしょう。
X |
Y |
x & y |
x | y |
true |
true |
true |
true |
false |
true |
false |
true |
false |
false |
false |
false |
null |
true |
null |
true |
null |
false |
false |
null |
?? 演算子
?? 演算子は、左側のオペランドが null 値でない場合にはこのオペランドを返し、null
値である場合には右側のオペランドを返します。
例1
nullableInteger = null;
Console.WriteLine((nullableInteger ?? -9999).ToString());
これは -9999を返す。
?? 演算子は、参照型にも適用可能です。
string str = "Hello World";
Console.WriteLine((str ?? "null だよ").ToString());
str = null;
Console.WriteLine((str ?? "null だよ").ToString());
これは、"nullだよ" と出力します。
名前空間のエイリアス修飾子 ( ::演算子 )
基本的には、Systemで始まるような名前空間や類似の名前空間を避ければ問題ないはずですが、大規模開発でどうしても名前空間がぶつかってしまう場合には、この方法でグローバル名前空間のルートから指定できるようになります。
例
using sys = System;
using Uchukamen;
namespace Uchukamen
{
class
Console
{
public
static
void
WriteLine()
{
//
Console.WriteLine("Hello World 1");
// これは、自分自身の Uchukamen.Console.WriteLine()
を呼び出してしまう。
// :: 演算子により、System.Console.WriteLine
を呼び出せるようになる。
//
global:: と指定した場合、グローバル名前空間のルートから検索してくれます。
global::System.Console.WriteLine("Hello
World 2");
// sys::Console
は、System.Console と同じ意味になります。
sys::Console.WriteLine("Hello
World 3");
}
}
class
program {
static
void
Main(string[]
args)
{
Uchukamen.Console.WriteLine();
global::System.Console.ReadLine();
}
}
}
静的クラス
静的メンバしか定義できないクラスを作ることが出来ます。
public
static
class
StaticClass
{
public
static
void
StaticMethod()
{
Console.WriteLine("StaticClass:
StaticMethod");
}
}
Visual Studio 2003
で静的クラスを宣言しようとすると、「名前空間にフィールドやメソッドのようなメンバを直接含めることはできません。」というエラーになります。
外部アセンブリのエイリアス
同じ完全修飾型名を持つ 2
つのアセンブリを参照するには、コンパイル時のオプションで次のようにエイリアスを指定します。
例
csc /r:GridV1=grid.dll
csc /r:GridV2=grid20.dll
これにより、外部エイリアスとして、GridV1 と GridV2 が定義されます。
extern キーワードを使用してプログラム内からこれらのエイリアスを参照することができます。
例
extern alias GridV1;
extern alias GridV2;
プロパティ アクセサのアクセシビリティ
VS2005 以降だと、プロパティの get, set
アクセサに対して、アクセシビリティ(private, protected, internal, public)
を指定できるようになりました。
VS 2003 だと、「修飾子をプロパティまたはイベント
アクセサ宣言に付属させることはできません。」というコンパイル時エラーになります。
private
int
myProp;
public
int
MyProperty
{
get {
return
myProp; }
protected
set
{ myProp = value; }
}
private int myProp;
public int MyProperty
{
protected get { return myProp; }
protected set { myProp = value; }
}
ただし、get と set 両方にアクセシビリティ修飾子を指定すると、
「アクセシビリティ修飾子は、プロパティまたはインデクサ 'App.MyProperty' の両方のアクセサに指定できません。」
というエラーになります。
デリゲートの共変性と反変性
デリゲートの共変性(Covariance)と反変性(Contravariance)
デリゲートの共変性(Covariance)
継承したデリゲートをベースクラスのデリゲートへ代入できる
例
class 哺乳類
{
}
class 犬: 哺乳類
{
}
class
Program
{
// Define the delegate.
public
delegate 哺乳類
HandlerMethod();
public
static 哺乳類
FirstHandler()
{
return
null;
}
public static
犬 SecondHandler()
{
return
null;
}
static void
Main()
{
HandlerMethod handler1 = FirstHandler;
// SecondHandler
は、犬のクラス
// HandlerMethod は、哺乳類のデリゲート
// しかし、共変性により、HanderMethod への SecondHandler の代入が可能になる。
// Covariance allows this delegate.
HandlerMethod handler2 = SecondHandler;
}
}
反変性(Contravariance)
ベースクラスのデリゲートを継承したデリゲートへ代入できる
using
System;
using System.Windows.Forms;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public
partial
class
Form1
: Form
{
public
Form1()
{
InitializeComponent();
this.textBox1.KeyDown
+= this.MultiHandler;
// KeyEventArgs
this.button1.MouseClick
+= this.MultiHandler;
// MouseEventArgs
}
private
void
textBox1_KeyDown(object
sender, KeyEventArgs e)
{
Trace.WriteLine("textBox1_KeyDown");
}
private
void
button1_MouseClick(object
sender, MouseEventArgs e)
{
Trace.WriteLine("button1_MouseClick");
}
private
void
MultiHandler(object
sender, System.EventArgs
e)
{
Trace.WriteLine("MultiHandler");
}
}
}
C# 1.0, 1.2 で、同じような実装をすると次のようになる。しかし、これは
「メソッド 'WindowsApplication1.Form1.MultiHandler(object,
System.EventArgs)' はデリゲート型 'void
System.Windows.Forms.KeyEventHandler(object,
System.Windows.Forms.KeyEventArgs)' と一致しません。」
というコンパイルエラーになってしまう。
this.textBox1.KeyDown += new
System.Windows.Forms.KeyEventHandler(this.MultiHandler);
this.button1.MouseDown += new
System.Windows.Forms.MouseEventHandler(this.MultiHandler);
C# 2.0 では、デリゲートの共変性(Covariance)と反変性(Contravariance)
が可能となり、実装の幅が広がっています。
デリゲートの宣言の簡素化
工事中
固定サイズ バッファ
C# 1.0, 1.2 では、C# 構造体が配列要素数を指定した配列を定義できず、C++
スタイルの固定サイズの構造体を宣言するのが困難でした。このため、とくに InterOp
の場合に、実装が難しいという問題がありました。
C# V2.0 では、fixed
キーワードにより、固定の配列を宣言することができます。
public struct
MyArray
{
public fixed
char pathName[128];
}
フレンド アセンブリ
アセンブリの internal なクラスや、メンバに、別のアセンブリからアクセスできます。
ライブラリ cs_friend_assemblies.dll をコンパイルし、
[assembly:
InternalsVisibleTo("cs_friend_assemblies_2")]
とすることにより、cs_friend_assemblies_2.exe
or cs_friend_assemblies_2.dll のアセンブリから、internal Class1, internal
なメソッド Class2.Test を呼び出すことができるようになる。
//
cs_friend_assemblies.cs
// compile with: /target:library
using
System.Runtime.CompilerServices;
using System;
[assembly:
InternalsVisibleTo("cs_friend_assemblies_2")]
// internal by default
class
Class1
{
public
void Test()
{
Console.WriteLine("Class1.Test");
}
}
// public type with internal member
public
class
Class2
{
internal
void
Test()
{
Console.WriteLine("Class2.Test");
}
}
もし、[assembly:
InternalsVisibleTo("cs_friend_assemblies_2")]
を指定しなかったり、呼び出し側のアセンブリ名が"cs_friend_assemblies_2"でなければ、次のようなエラーになり、アクセスができません。
「エラー 1 'Class1' はアクセスできない保護レベルになっています。 」
「エラー 5 'Class2' に 'Test' の定義が含まれておらず、型
'Class2' の最初の引数を受け付ける拡張メソッドが見つかりませんでした。using
ディレクティブまたはアセンブリ参照が不足しています。 」
#pragma 警告
#pragma warning を使用すると、特定の警告を有効または無効にできます。
例
using System;
public
class
TestPragma
{
static
void Main()
{
int
x = 0;
#pragma warning disable 219
int
y = 0;
#pragma warning restore 219
int
z = 0;
}
}
変数 x, z は、次の警告になります。
警告 1 変数 'x' は割り当てられていますが、その値が使用されていません。
警告 2 変数 'z' は割り当てられていますが、その値が使用されていません。
しかし、変数 y は、警告は発生しません。
volatile
volatile キーワードは、lock
による排他制御がおこなわれていないが、複数のスレッドからアクセスされる可能性がある変数に対して、JIT
コンパイラーに対して、コンパイラによる最適化を行わないように指示します。このため、フィールドには常に最新の値が含まれます。
通常に volatile なしでコンパイルすると、MSIL
は、次のようなコードを生成します。
int
x = 0;
.field private int32 x
一方、volatile
を指定してコンパイルすると、MSIL は次のように変数は揮発性(IsVolatile)であるため、最適化しないように指示されます。
volatile
int
y = 0;
.field private int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile)
y
C# 3.5の主な追加機能
LINQ - Language-Integrated Query
.Net Framework 3.5から、LINQ が追加されました。
従来のデータクエリーの問題点
-
クエリは単純な文字列として表され、コンパイル時の型チェックが行えない
-
IntelliSense のサポートは利用できない。
-
SQL データベース、XML ドキュメント、さまざまな Web サービスなど、データ
ソースの種類ごとに異なるクエリ言語。
LINQ のメリット
-
完全な型チェック
-
IntelliSense のサポート
-
データソースによらない統合されたクエリ言語
言語拡張
var クエリ式
var
query =
from num
in
numbers
where num > 3
orderby num
select num;
var 暗黙的に型指定されるローカル変数
(Implicitly Typed Local Variables)
最適な型をコンパイラが決定した、強く型付けされた変数。
注意: バリアント型や、弱い型付けされた型や、遅延バインディングではない。
注意: varの使用は、コードが理解しづらくなる可能性があるので、必要最小限にしたほうが良い。
例
var i =
1; // int
var s = "Hello World";
// string
var expr =
// IEnumerable<Customer>
from c in customers
where c.City == "Tokyo"
select c;
var
s = "abc";
s = 1; // エラー 1 型 'int' を型 'string' に暗黙的に変換できません。
コンパイラーが静的に型推論を行ってくれる。この例だと、s に "abc"
という文字列を代入しているので、s は string 型であると推論できる。したがって、この変数 s に、1
を代入しようとすると、「型 'int' を型 'string' に暗黙的に変換できません。」 というエラーになる。
var 暗黙に型指定される配列
var
a = new[]
{ 0, 1, 2 }; // 変数 a は整数の配列と型推論される。
var 匿名型 (anonymous type)
var
person = new
{ Name = "Uchukamen",
Age = 34 };
int
age = person.Age;
string
name = person.Name;
オブジェクト初期化子とコレクション初期化子
Customer cust =
new
Customer { Name = "Tom",
Phone = "555-5555"
};
例
namespace ConsoleApplication
{
class
Program
{
class Customer
{
public
string Name { get;
set; }
public
string Phone { get;
set; }
}
static void
Main(string[] args)
{
Customer cust =
new Customer { Name =
"Tom", Phone = "555-5555"
};
}
}
}
拡張メソッド
拡張メソッドは型に関連付けることができる静的メソッドで、その型のインスタンス
メソッドと同じように呼び出すことができる。この機能を使用すると、既存の型を実際に変更しなくても、その型に新しいメソッドを実質的に
"追加" できます。標準クエリ演算子は、IEnumerable<(Of <(T>)>) を実装する任意の型で LINQ
クエリ機能を実現する拡張メソッドのセットになっている。
詳細については、MSDN の「拡張メソッド (C# プログラミング ガイド)」を参照。
ラムダ式
ラムダ式は、=>
演算子を使用して関数本体から入力パラメータを分離するインライン関数であり、コンパイル時にはデリゲートまたは式ツリーに変換されます。LINQ
プログラミングでは、標準クエリ演算子に対する直接メソッド呼び出しを行う場合にラムダ式を使用します。
自動実装プロパティ
次の例のようなプロパティを宣言すると、コンパイラによってプライベートな匿名バッキング
フィールドが作成されます。プライベートな匿名バッキング フィールドには、プログラムからはアクセスできません。
例: public string Name {get; set;}
LINQで扱えるデータソース
-
配列
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
-
XML
XElement contacts = XElement.Load(@"c:\myList.xml");
-
データベース
DataContext db = new DataContext(@"c:\northwnd.mdf");
LINQ の例
int[]
numbers = { 0, 1, 2, 3, 4, 5, 6 };
var query =
from v in numbers
where v > 3
select v;
foreach (var i
in query)
{
Console.WriteLine(i);
}
注意: 遅延実行
var query = で定義するクエリ変数自体が行うのはクエリ コマンドの格納のみです。
実際のクエリの実行は、foreach ステートメントで呼び出されるまで処理が延期されます。
クエリの即時実行
クエリは遅延実行で定義されていますが、Count, Sum
などの集計関数によりクエリを即時実行することができます。
int[]
numbers = new
int[7]
{ 0, 1, 2, 3, 4, 5, 6 };
var
query =
from v
in numbers
where v > 3
select v;
Console.WriteLine(query.Sum());
また、ToArray, ToList により、即時実行させることもできます。
int[]
array = query.ToArray();
List<int> list =
query.ToList();
クエリ構文
例
using
System;
using System.Linq;
using System.Collections.Generic;
class
LINQQueryExpressions
{
static
void Main()
{
int[]
scores = { 97, 92, 81, 60 };
IEnumerable<int>
scoreQuery =
from
score in
scores
where
score > 80
select
score;
foreach (int
i in
scoreQuery)
{
Console.WriteLine(i
+ " ");
}
Console.ReadLine();
}
}
クエリ構文とメソッド構文
通常は、クエリ構文の方が単純で読みやすいため、クエリ構文を使用することをお勧めします。
.NET CLRには、クエリ構文の概念はありません。したがって、クエリ式は、コンパイル時に
CLR が理解できる形式、つまりメソッド呼び出しに変換されます。
したがって、次のクエリ構文は、
int[]
numbers = { 5, 10, 8, 3, 6, 12};
//Query syntax:
IEnumerable<int>
numQuery1 =
from
num in
numbers
where
num % 2 == 0
orderby
num
select
num;
次のメソッド構文と同じです。
//Method
syntax:
IEnumerable<int>
numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
このメソッド構文には、拡張メソッド (Where と OrderBy)、ラムダ式(num
=> num % 2 == 0 と、n => n) が使われています。
拡張メソッド(Extension Method)
Where と OrderBy メソッドは、System.Linq
名前空間で定義されているメソッドが拡張されているものです。
独自に拡張メソッドを追加することも可能で、次の例は int に Cube という
3乗するメソッドを拡張した例。
using
System;
using System.Linq;
namespace CustomExtensions
{
public
static
class
MathExtension
{
public
static
int
Cube(this
int
i)
{
return
i*i*i;
}
}
}
namespace Extension_Methods_Simple
{
using
CustomExtensions;
class
Program
{
static
void
Main()
{
int
i = 5;
int
j = i.Cube();
Console.WriteLine("{0}
の3乗は {1}", i, j);
Console.ReadLine();
}
}
}
ラムダ式
ラムダ式とは、式とステートメントを含めることができ、計算を実行して単一の値を返す、名前を持たない匿名関数です。これにより、デリゲート型または式ツリー型を作成することができます。
ラムダ式は、デリゲートを作成するための最も便利な方法です。
前の例で、Where(num => num % 2 == 0) は、条件式
(num % 2 == 0) がインライン引数として Where メソッドに渡されています。ここで、=> が ラムダ演算子です。
ラムダ式には、式形式と、ステートメント形式の2種類があります。
式形式のラムダ
右辺に式があるラムダ式を式形式のラムダと呼びます。
式形式のラムダは、式ツリーの構築に幅広く使用されます。
式形式のラムダは式の結果を返します。
書式: (input parameters) => expression
例
x => x * x
(x, y) => x == y
注意: かっこはラムダの入力パラメータが 1 つの場合のみ省略可能
using
System;
using System.Linq;
namespace Lambda
{
class
Program
{
delegate
int
Calc1(int
x);
delegate
int
Calc2(int
x, int
y);
static
void
Main(string[]
args)
{
Calc1 calc1 = (x) => x * 2;
Console.WriteLine(calc1(4));
// 引数が1つの場合は、括弧を省略可能
calc1 = x => x * 3;
Console.WriteLine(calc1(4));
Calc2 calc2 = (x, y) => x * y;
Console.WriteLine(calc2(4,5));
Console.ReadLine();
}
}
}
ステートメント形式のラムダ
書式: (input parameters) => {statement;}
ステートメント形式のラムダの本体は任意の数のステートメントで構成できます。現実的には、2、3
個以下にします。
using
System;
using System.Linq;
namespace Lambda
{
class
Program
{
delegate
string
StatementLambda(string
str);
static
void
Main(string[]
args)
{
StatementLambda sLambda = n =>
{
string s =
n + " + World";
Console.WriteLine(s);
return
"return value = "
+ s;
}
string
retVal = sLambda("Hello");
Console.WriteLine(retVal);
Console.ReadLine();
}
}
}
汎用デリゲートを使ったラムダ式
ラムダ式は、計算を実行して単一の値を返すので、汎用デリゲートを活用すると
デリゲートを個別に宣言せずにすむ。
特に、System.Linq 名前空間に存在する型の多くのメソッドには、Func<T, TResult>型が多いため、デリゲートを明示的に宣言することなく、これらのメソッドにラムダ式を渡すことができる。
Func<TResult>
public delegate TResult Func<TResult>()
パラメータを受け取らずに、TResult パラメータに指定された型の値を返す
Func<T, TResult>
public delegate TResult Func<T, TResult>(T arg)
1 つのパラメータを受け取って TResult パラメータに指定された型の値を返す
同様に、次の汎用デリゲートがある。
Func<T1, T2, TResult>
Func<T1, T2, T3, TResult>
Func<T1, T2, T3, T4, TResult>
//
delegate
int
MyDeleg(int
n);
static
void
StatementLambdaWithoutGenericDelegate()
{
MyDeleg myDel = n => n * n;
Console.WriteLine(myDel(6));
}
// 汎用デリゲートを使用すると
static
void
StatementLambdaAndGenericDelegate()
{
Func<int,
int>
myDel = n => n * n;
Console.WriteLine(myDel(7));
}
というように、すこし簡潔に書ける。
標準クエリ演算子でのラムダ
ラムダ式
static
void
LambdaInStandardQueryOperators()
{
int[]
numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
foreach
(var
x in
numbers.Where(n => n > 5))
Console.WriteLine(x);
}
// TakeWhile Do
Until
static
void
TakeWhile()
{
int[]
numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
foreach
(var
x in
numbers.TakeWhile(n => n < 6))
Console.WriteLine(x);
}
System.Linq.Queryable
IQueryable(T) を実装するデータ構造を照会するための一連の static
メソッドを提供します。 このメンバーには、Count, Distinct, First, GroupBy, Join,
Last, Max, Min, OrderBy, Select, Take, TakeWhile, ThenBy,
ThenByDescending, Union, Where などのメソッドが提供されています。
これにより、次のような実装が可能になります。
int[]
numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
Console.WriteLine("Count
: " + numbers.Count(n => n > 3));
Console.WriteLine("First
: " + numbers.First(n => n > 5));
foreach
(int
x in
numbers.Where(n => n > 5))
Console.WriteLine(x);
ここで、次のメソッド構文をクエリ構文に変換すると
IEnumerable
<int>
query = from
i in
numbers
where
i > 5
select
i;
foreach
(int
x in
query)
Console.WriteLine(x);
となります。これはさらに次のように短く書くことができます。
foreach
(int
x in
from
i in
numbers where
i > 5 select
i)
Console.WriteLine(x);
OrderBy と OrderByDescending
string[]
names = { "Miku",
"Ren",
"Rin",
"Uchukamen"};
var orderedNames = names.OrderBy(s => s);
foreach
(string
item in
orderedNames)
Console.WriteLine(item);
var orderByDescendingsNames =
names.OrderByDescending(s => s);
foreach
(string
item in
orderByDescendingsNames)
Console.WriteLine(item);
GroupBy
string
[] names = {
"Miku",
"Ren",
"Rin",
"Uchukamen"};
var groups
= names.GroupBy(s => s.Length, s => s[0]);
foreach
(IGrouping<int,
char>
group in
groups)
{
Console.WriteLine("Strings
of length {0}", group.Key);
foreach (char
value in
group)
Console.WriteLine("
{0}", value);
}
LINQ - データソースの利用
XMLデータソース
List から XMLに変換する場合の処理をしようと思うと、XmlDocument,
XmlElementを
使用して、次のようになる。
using System;
using System.Collections.Generic;
using System.Xml;
namespace ConsoleApplication2
{
class Program
{
class Student
{
public string First;
public string Last;
public int ID;
public List Scores;
}
static void Main(string[] args)
{
List students = new List()
{
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List{97, 92, 81, 60}},
new Student {First="Claire", Last="O’Donnell", ID=112, Scores = new List{75, 84, 91, 39}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List{88, 94, 65, 91}},
};
XmlDocument xmlDoc = new XmlDocument();
XmlElement root = xmlDoc.CreateElement("Root");
xmlDoc.AppendChild(root);
foreach (Student std in students)
{
XmlElement first = xmlDoc.CreateElement("First");
first.InnerText = std.First;
XmlElement last = xmlDoc.CreateElement("Last");
last.InnerText = std.Last;
XmlElement id = xmlDoc.CreateElement("ID");
id.InnerText = std.ID.ToString();
string scoreStr = String.Format("{0},{1},{2},{3}", std.Scores[0],
std.Scores[1], std.Scores[2], std.Scores[3]);
XmlElement scores = xmlDoc.CreateElement("Score");
scores.InnerText = scoreStr;
XmlElement student = xmlDoc.CreateElement("student");
student.AppendChild(first);
student.AppendChild(last);
student.AppendChild(id);
student.AppendChild(scores);
root.AppendChild(student);
}
xmlDoc.Save("test.xml");
Console.ReadKey();
}
}
}
これを LINQ を使うと次のように、かなり見通しが良くなる。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Test();
}
class Student
{
public string First;
public string Last;
public int ID;
public List Scores;
}
static List students = new List()
{
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores = new List{97, 92, 81, 60}},
new Student {First="Claire", Last="O’Donnell", ID=112, Scores = new List{75, 84, 91, 39}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores = new List{88, 94, 65, 91}},
};
static void Test()
{
var studentsToXML = new XElement("Root",
from student in students
let x = String.Format("{0},{1},{2},{3}", student.Scores[0],
student.Scores[1], student.Scores[2], student.Scores[3])
select new XElement("student",
new XElement("First", student.First),
new XElement("Last", student.Last),
new XElement("Scores", x)
) // end "student"
); // end "Root"
Console.WriteLine(studentsToXML);
Console.ReadKey();
}
}
}
注意: let句を使用して、サブ式の結果を格納して後の句で使用することができる。