2014.05.10
他所で使った資料がモトなので,ウチの開発とは関係ない部分もあります.
- VS.NET 2003 To 2005
C++のテンプレート/Javaのジェネリック
どういう時に使う?
- Integer型のみのコレクションが欲しい
- String型のみのコレクションも欲しい
Class IntArray
Private array As New ArrayList
Public Sub Add(ByVal data As Integer)
array.Add(data)
End Sub
Public Function ToArray() As Integer()
Return DirectCast(array.ToArray(GetType(Integer)), Integer())
End Function
End Class
Class StringArray
Private array As New ArrayList
Public Sub Add(ByVal data As String)
array.Add(data)
End Sub
Public Function ToArray() As String()
Return DirectCast(array.ToArray(GetType(String)), String())
End Function
End Class
'クラス間の差は,型だけしかない⇒DRYじゃない.
Class TypeArray(Of T)
Private array As New ArrayList
Public Sub Add(ByVal data As T)
array.Add(data)
End Sub
Public Function ToArray() As T()
Return DirectCast(array.ToArray(GetType(Type)), T())
End Function
End Class
'クラスを一つ作るだけでいい
'使い方
Sub Method()
Dim intArray As New TypeArray(Of Integer)
Dim strArray As New TypeArray(Of String)
intArray.Add(10) ' OK
intArray.Add("10") ' BuildError
End Sub
実は.NET Frameworkもジェネリック対応になっているので,型指定コレクションは標準で用意されています.
Sub Method()
'コレだけで超多機能な型指定コレクションが使えます
Dim intArray As New List(Of Integer)
End Sub
余談
VB.NET 2003 で List(Of String)と同等のコレクションクラス (String型のみを格納する標準的な機能(Count, IndexOf, Insert, Remove等)を持つコレクションクラス) を作るには?
'IListを継承する
Class MyCollection
Implements IList
Class MyCollection
Implements IList
Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) Implements System.Collections.ICollection.CopyTo
End Sub
Public ReadOnly Property Count() As Integer Implements System.Collections.ICollection.Count
Get
End Get
End Property
Public ReadOnly Property IsSynchronized() As Boolean Implements System.Collections.ICollection.IsSynchronized
Get
End Get
End Property
Public ReadOnly Property SyncRoot() As Object Implements System.Collections.ICollection.SyncRoot
Get
End Get
End Property
Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
End Function
Public Function Add(ByVal value As Object) As Integer Implements System.Collections.IList.Add
End Function
Public Sub Clear() Implements System.Collections.IList.Clear
End Sub
Public Function Contains(ByVal value As Object) As Boolean Implements System.Collections.IList.Contains
End Function
Public Function IndexOf(ByVal value As Object) As Integer Implements System.Collections.IList.IndexOf
End Function
Public Sub Insert(ByVal index As Integer, ByVal value As Object) Implements System.Collections.IList.Insert
End Sub
Public ReadOnly Property IsFixedSize() As Boolean Implements System.Collections.IList.IsFixedSize
Get
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.IList.IsReadOnly
Get
End Get
End Property
Default Public Property Item(ByVal index As Integer) As Object Implements System.Collections.IList.Item
Get
End Get
Set(ByVal Value As Object)
End Set
End Property
Public Sub Remove(ByVal value As Object) Implements System.Collections.IList.Remove
End Sub
Public Sub RemoveAt(ByVal index As Integer) Implements System.Collections.IList.RemoveAt
End Sub
End Class
'とっても面倒くさい
IListを実装せずにオレオレコレクションクラスで良いのでは?
⇒みんな大好きForEach文が使えない.Sortができない.Filterができない⇒使いづらい⇒使われない
余談2
For Eachが使える場面でまさかForなんて使ってませんよね?
For Each文を使うには?
⇒IEnumerableを実装すれば使えます(面倒くさいけど)
Class MyCollection
Implements IEnumerable
Private _array As New ArrayList
Public Sub Add(ByVal o As Object)
_array.Add(o)
End Sub
Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return New MyCollectionEnumerator(_array)
End Function
Private Class MyCollectionEnumerator
Implements IEnumerator
Private _index As Integer = -1
Private ReadOnly _array As ArrayList
Public Sub New(ByVal array As ArrayList)
_array = array
End Sub
Public ReadOnly Property Current() As Object Implements System.Collections.IEnumerator.Current
Get
Return _array(_index)
End Get
End Property
Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
_index += 1
Return _index <> _array.Count
End Function
Public Sub Reset() Implements System.Collections.IEnumerator.Reset
_index = -1
End Sub
End Class
End Class
※ VisualStudio2012からは,IEnumerableの実装がクッソ簡単になっています.上記は忘れてください.
- IDbDataReader(OracleDataReaderとかの抽象クラス)を使い終わったらDispose()を必ず呼びたい
Dim rdr As OracleDataReader
Try
dllxv.XVFOPNRDR(rdr, sql)
' do something
Finally
rdr.Dispose()
End Try
- 忘れがち.
- リソースリークは気づきにくい(でも少しずつ被害が・・・)
- 例外を記述するための構文なのにリソース管理に用いるのは普通じゃない(けど慣例化している)
Using rdr
' do something
End Using 'ここでrdr.Dispose()が例外の有無に関係なく必ず呼ばれる
コマンドラインビルドが簡単になる.
CIやるなら最低限これくらいは欲しい.
VB.NET 2003ならdevenvに引数を渡しまくってようやくビルドできる
APM(Asynchronous Programming Model)のみ
Dim asyncHoge = New dlgtAsync(AddressOf subAsyncHoge)
Dim req = WebRequest.Create("http://hoge")
req.BeginGetResponse(asyncHoge, req)
Delegate Function dlgtAsync(ByVal ar As IAsyncResult)
Sub subAsyncHoge(ByVal ar As AsyncResult)
Dim res = (ar.AsyncState).EndGetResponse(ar)
End Sub
EAP(Event-based Asynchronous Pattern)が使える
Dim c As New WebClient
c.Encoding = Encoding.UTF8
AddHandler c.DownloadStringCompleted, AddressOf subWriteString
c.DownloadStringAsync(New Uri("http://hoge"))
Sub subWriteString(ByVal sender As Object, ByVal args As DownloadStringCompletedEventArgs)
Dim result = args.Result
Console.WriteLine(result)
End Sub
- ジェネリクス対応(最大の変更点)
- FTP送受信のクラス.(あまり使わないと思いますが…)
- ASP.NETの大幅強化.(とっても重要な強化だけど,伝わらないと思うので省略)
- ADO.NET 2.0(データプロバイダ(Oracle,SQLServer,MySQL,XMLDataSetなど)に依存しないコードが書ける)
- イテレータ (VB.NETにポートされるのは2012になってから)
- Yield
- 匿名関数 (VB.NETにポートされるのは2008から)
- function(){}
Dim num As Integer = 10
As Integer 書くのが面倒.
でも遅延バインディングを有効にしてしまうと, 型に起因するエラーがランタイムにしかわからない,ボクシングが発生して遅い.
Dim num = 10 ' TypeOf num Is Object
Dim str = SubString(num, 10) ' ランタイムエラー(ビルドはできる)
Dim num = 10 ' TypeOf num Is Integer
Dim SubString(num, 10) ' ビルドエラー(ビルド前にもIntellisenseが教えてくれる)
以下のコードは,遅延バインディングではありません
For Each v In Values
Debug.WriteLine(v)
Next
構造体やクラスのパブリックメンバの初期化がとっても楽になる.
Structure A
Public x As Integer
Public y As Integer
End Structure
Dim a As A
a.x = 10
a.y = 20
Dim a As New A With {.x = 10, .y = 20}
余談 C#ではもっと読みやすい
A a = A{ x = 10, y = 20 };
Dim a As New {.x = 10, .y = 20}
- 例えば,メソッドの戻り値や引数で,いくつかの変数をまとめて渡したい時に使う.
- 後述のLINQで多用する.
- 遅延バインディングでは無い
HogeMethod(strPatno, New {.name="白十字花子", .birthday="19500101" } )
- クラスのメソッドを,クラスの外に書ける.
- というより,第1引数のメソッドを書ける
- 既存のコードを変更することができないクラス・インターフェイスを拡張できる
Dim str As String
' str.IsNotNothing() というメソッド呼び出しをしたい!
<Extension()>
Public Function IsNotNothing(ByVal str) As Boolean
Return Not(str Is Nothing)
End Function
str.isNotNothing() ' ビルドOK
- 使い捨ての関数
- 関数オブジェクトをメソッド内で直ぐに作れる.
いろんな場面で重宝するが,コードが読みづらくなる.C#のラムダ式が欲しくなる.
VB.NET 2008のLINQでよく使う.ただし,VB.NET 2010以降はラムダ式の方を主に使うので使用頻度は低い.
Delegate Sub Action()
Sub WriteHoge()
Debug.WriteLine("Hoge")
End Sub
Sub Main()
Dim act As New Action(AddressOf WriteHoge)
act()
End Sub
Delegate Sub Action()
Sub Main()
Dim act As Action = _
Sub()
Debug.WriteLine("Hoge")
End Sub
act()
End Sub
メソッドの中身を別ファイルに記述できる.
Language INtegrated Query
VisualStudio 2008 の最大の変更点.(LINQ前,LINQ後と呼ばれることもあるらしい)
'準備---------------------
Class Hoge
Public id As Integer
Public name As String
Public skill As New ArrayList
Public Sub New(ByVal i, ByVal n, ByVal s)
id = i
name = n
skill = s
End Sub
End Class
Dim hogeCollection As New ArrayList
hogeCollection.Add(New Hoge(0, "花子", Nothing))
hogeCollection.Add(New Hoge(1, "太郎", Nothing))
hogeCollection.Add(New Hoge(2, "次郎", Nothing))
'クエリ構文-------------------
q = From h In hogeCollection
Where h.name.Contains("郎")
Select h.name
For Each e In q
Debug.WriteLine(e)
End For
' デバッグ出力------
' 太郎
' 次郎
'メソッド構文(VB.NET 2008)---------
q = hogeCollection
.Where( Function (x As Hoge)
Return x.name.Contains("郎")
End Function )
.Select(Function (x As Hoge)
Return x.name
End Function)
For Each e In q
Debug.WriteLine(e)
End For
' 太郎
' 次郎
' 匿名関数を使わない場合
q = hogeCollection
.Where(dlgtContains("郎"))
.Select(dlgtGetName)
Windows.Formsに代わる,UIとロジックを分離して記述できるGUIフレームワーク.
Expression Blend という,UIデザインのみを作りこめるツールも標準である
- PowerPacks
- If式
など
個人的にはIf式は大きな変更
Class C
Private _str As String
Public Property Str() As String
Get
Return _str
End Get
Set(ByVal Value)
_str = Value
End Set
End Property
End Class
Class C
Public Property Str As String
End Class
引数リストが長いとき,複数行に分けて書きたい
Public Sub Hoge(ByVal a As Integer, ByVal b As Integer, ByVal c As Integer)
Public Sub Hoge(ByVal a As Integer, _
ByVal b As Integer, _
ByVal c As Integer)
### VB.NET 2010
Public Sub Hoge(ByVal a As Integer,
ByVal b As Integer,
ByVal c As Integer)
待望の匿名関数の強化版
'ラムダ式
Function(x) x + 1
'匿名関数
Function(ByVal x)
Return x + 1
End Function
※余談 C#のラムダ式
x => x + 1
var linq = hoge.Where(x => 0 <= x.id && x.id <= 10)
.Where(x => x.kbn == 10)
.Where(x => x.ymd <= 20130828)
.Select(x => x.name.Trim)
var linq = hoge.Where(Function(x) 0 <= x.id AndAlso x.id <= 10)
.Where(Function(x) x.kbn = 10)
.Where(Function(x) x.ymd <= 20130828)
.Select(Function(x) x.name.Trim)
- DataViewと違って,インテリセンスが完全に効く
- DataViewよりパフォーマンスがいい(Boxingが発生しない,式の評価が遅延される)
- DataViewよりも多機能(Count, Sum, Aggregate, SelectMany, Join, Uniqueなど)
.NET Framework4.0からTaskクラスが追加され,TAP(Task-based Asynchronous Pattern)が主流に.
現在の非同期処理もTAPが主流です.前述のAEP,EAPは駆逐されました.完全に忘れていいです.
Task.Factory.StartNew(New Addressof SetButtonEnableFalse) .ContinueWith(New Addressof Sleep30Sec) .ContinueWith(New Addressof SetButtonEnableTrue, TaskScheduler.FromCurrentSynchronizationContext())
- 複数行のラムダ式
- コレクション初期化子
- Dim nums As New List(Of Integer) From {1, 2, 3, 4}
- 動的言語ランタイム
- Option Strict On でも遅延バインディングが任意の場所で使える
- ジェネリック型の分散
余談
この頃からLINQを非同期に記述できる,**Rx(Reactive Extensions)**が流行しだした.
.NET Frameworkには含まれていないが,Microsoftの正規プロダクト
現在でももちろん使われている素晴らしい技術
例えばこういうことができます.
Observable
.FromEventPattern(Me.btnOK, "Click") 'ボタンのClickイベントがRaiseされたら
.Do(New AddressOf SetEnableFalse) 'ボタンをEnable=Falseにする
.ObserveOn(Scheduler.ThreadPool) 'その後別スレッドの監視を開始し
.Do(New AddressOf DoSomething) '何かしら時間のかかる作業を行う
.ObserveOn(SynchronizationContext.Current) '最初のスレッドの監視を再開し
.Subscribe(New AddressOf SetEnableTrue) 'ボタンを有効にする
- LINQと同じ位の衝撃
- 非同期プログラムが革新的に簡単に書ける(同期的にかける).
- Reactive Extensionsと比べても楽
Async Sub btnOK_Click(sender As Object, e As RoutedEventArgs)
Me.btnOK.IsEnabled = False
Await Task.Run(New AddressOf DoSomething)
Me.btnOK.IsEnabled = True
End Sub
---
IEnumerable(Of T)の実装が驚くほど簡単になる.つまりLINQ対応のコレクションクラスが簡単に作れる.
こちらも革新的.C#が2005で導入した機能がようやくVBにポーティングされた. LINQと合わさって最強に見える.
Class HogeCollection
Implements IEnumerable(Of Hoge)
Iterator Function GetEnumerator() As IEnumerator(Of Hoge)
Yield Hoge
End Function
Yield呼ぶだけ!
End Class
コレだけで,For Each文が使える.LINQも使える.すばらしい.
コレクションクラス作るのが面倒だからDataTableにデータ突っ込んでDataViewでゴチャゴチャやる.ということが減らせる.
デフォルトで引数ByValで渡されるのでわざわざ書かなくていい.地味にありがたい
ByRefは書いてね
IEnumerable(Of T)を実装する全てのコレクション処理で使える.使うべき.
例えば,VB.NET 2003 で良くある処理
dtv.RowFilter = "ORD_INF_KBN = '20' "
For Each row As DataRowView In dtv
Debug.WriteLine(CStr(row("PATNO")))
Next
このコードの潜在的な危険性
- dtvに ORD_INF_KBN 列は存在するか?
- ORD_INF_KBN 列は String型 なのか?
- dtvに PATNO 列は存在するか?
- CStr(row("PATNO"))で正しくキャストできるか?
たった4行で4つもランタイムにしかわからないバグの可能性が潜んでいるし,コードを書く際,dtvに何の列が存在するのか覚えていないとコードが書けない,読めない.
linq.Where(Function(x) x.Kbn = '20')
.WriteLine(Function(x) x.Patno )
Class Hoge
Public Kbn As String
Public Patno As String
End Class
DataViewの問題が(一応)全て解決している.
- dtvに ORD_INF_KBN 列は存在するか?
- 存在するプロパティ(Public Property Kbn As String)以外を指定するとビルドエラー
- ORD_INF_KBN 列は String型 なのか?
- KbnプロパティはString型
- dtvに PATNO 列は存在するか?
- 存在するプロパティのみ指定可能
- CStr(row("PATNO"))で正しくキャストできるか?
- PatnoプロパティはString型なのでキャスト不要
- ジェネリックの使い方に慣れてください.
- ラムダ式の使い方に慣れてください.
- リソース管理はUsingを使ってください.
- 型推論は強力なので頼ってください.
- 型推論は遅延バインディングではありません
- LINQはとっても便利なので使い方に慣れてください.
- オブジェクト初期化子,コレクション初期化子も便利です.
- 複数行にまたがるコードでも "_" が不要になる場合が多くあります.
- 非同期処理はAsync/Awaitを真っ先に検討してください.
OSSの紹介
- Dapper
- とっても便利なMicroORM
- ReactiveExtensions
- 非同期LINQ
学習には以下のサイトが非常に有効です.
C#によるプログラミング入門 | ++C++;// 未確認飛行 C
http://ufcpp.net/study/csharp/
.NET Framework | ++C++;// 未確認飛行 C
http://ufcpp.net/study/dotnet/index.html
C#のサイトですが,現在C#の機能の8割はVBにポートされているので気にせず読みましょう.