Skip to content

Instantly share code, notes, and snippets.

@aliyome
Last active July 22, 2018 06:04
Show Gist options
  • Save aliyome/2214bac5e3e0ec102cdf to your computer and use it in GitHub Desktop.
Save aliyome/2214bac5e3e0ec102cdf to your computer and use it in GitHub Desktop.
Modern .NET Programming

Modern Visual Basic

2014.05.10


注意

他所で使った資料がモトなので,ウチの開発とは関係ない部分もあります.


  1. VS.NET 2003 To 2005

Generic Programming

C++のテンプレート/Javaのジェネリック

どういう時に使う?

  • Integer型のみのコレクションが欲しい
  • String型のみのコレクションも欲しい

VB.NET 2003

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じゃない.

VB.NET 2005

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()を必ず呼びたい

VB.NET 2003

Dim rdr As OracleDataReader

Try
    dllxv.XVFOPNRDR(rdr, sql)
	' do something
Finally
	rdr.Dispose()
End Try

  • 忘れがち.
  • リソースリークは気づきにくい(でも少しずつ被害が・・・)
  • 例外を記述するための構文なのにリソース管理に用いるのは普通じゃない(けど慣例化している)

VB.NET 2005

Using rdr
	' do something
End Using 'ここでrdr.Dispose()が例外の有無に関係なく必ず呼ばれる

MSBuild

コマンドラインビルドが簡単になる.

CIやるなら最低限これくらいは欲しい.

VB.NET 2003ならdevenvに引数を渡しまくってようやくビルドできる


非同期処理

VB.NET 2003

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

VB.NET 2005

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

.NET Framework 2.0

  • ジェネリクス対応(最大の変更点)
  • FTP送受信のクラス.(あまり使わないと思いますが…)
  • ASP.NETの大幅強化.(とっても重要な強化だけど,伝わらないと思うので省略)
  • ADO.NET 2.0(データプロバイダ(Oracle,SQLServer,MySQL,XMLDataSetなど)に依存しないコードが書ける)

余談 C# 2.0 ( VB.NET 2005と同時期 )

  • イテレータ (VB.NETにポートされるのは2012になってから)
    • Yield
  • 匿名関数 (VB.NETにポートされるのは2008から)
    • function(){}

VB.NET 2008



ローカル型推論

Dim num As Integer = 10

As Integer 書くのが面倒.

でも遅延バインディングを有効にしてしまうと, 型に起因するエラーがランタイムにしかわからない,ボクシングが発生して遅い.


VB.NET 2003

Dim num = 10  ' TypeOf num Is Object
Dim str = SubString(num, 10)  ' ランタイムエラー(ビルドはできる)

VB.NET 2008

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

VB.NET 2003

Dim a As A
a.x = 10
a.y = 20

VB.NET 2008

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引数のメソッドを書ける
  • 既存のコードを変更することができないクラス・インターフェイスを拡張できる

VB.NET 2008

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以降はラムダ式の方を主に使うので使用頻度は低い.


VB.NET 2003

Delegate Sub Action()
Sub WriteHoge()
	Debug.WriteLine("Hoge")
End Sub

Sub Main()
	Dim act As New Action(AddressOf WriteHoge)
	act()
End Sub

VB.NET 2008

Delegate Sub Action()

Sub Main()
	Dim act As Action = _
		Sub()
			Debug.WriteLine("Hoge")
		End Sub

	act()
End Sub

部分メソッド

メソッドの中身を別ファイルに記述できる.


LINQ

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)

WPF

Windows.Formsに代わる,UIとロジックを分離して記述できるGUIフレームワーク.

Expression Blend という,UIデザインのみを作りこめるツールも標準である


その他の変更点

  • PowerPacks
  • If式

など

個人的にはIf式は大きな変更


VB.NET 2010


自動実装プロパティ

VB.NET 2003

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

VB.NET 2010

Class C
	Public Property Str As String
End Class

暗黙の行連結

引数リストが長いとき,複数行に分けて書きたい

Public Sub Hoge(ByVal a As Integer, ByVal b As Integer, ByVal c As Integer)

VB.NET 2003

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

LINQ + ラムダ式 = すごい便利!

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) 'ボタンを有効にする

VB.NET 2012

Async-Await

  • LINQと同じ位の衝撃
  • 非同期プログラムが革新的に簡単に書ける(同期的にかける).
    • Reactive Extensionsと比べても楽

VB.NET 2012

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

---

Yield

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不要

デフォルトで引数ByValで渡されるのでわざわざ書かなくていい.地味にありがたい

ByRefは書いてね


結局LINQってどういう場面で使うの?

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

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型なのでキャスト不要

VS2003 To VS2013 簡易まとめ

  • ジェネリックの使い方に慣れてください.
  • ラムダ式の使い方に慣れてください.
  • リソース管理は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にポートされているので気にせず読みましょう.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment