Hatena::ブログ(Diary)

明日の鍵

2009-12-18

VB.NETにてGenericが使えないときのtips

問題

.NET Framework 1.1 VB.NETで開発するとGenericが使えないので、*1

コレクションを使いたいときはキャストしてあげないといけない。

しかしVB.NETでキャストを書くと

Dim order As Order = CType(OrderList(i),Order)

っていう風に長くなるので量が多いとめんどう。

対策

ArrayListを拡張することで毎回キャストする手間が省ける

OrderList
Option Explicit On 
Option Strict On

Public Class OrderList
  Inherits ArrayList

  Default Public Shadows ReadOnly Property Item(ByVal index As Integer) As Order
    Get
      Return CType(MyBase.Item(index), Order)
    End Get
  End Property

  Public Shadows Function Add(ByVal value As Order) As Integer
    Return MyBase.Add(value)
  End Function

  Public Shadows Sub Insert(ByVal index As Integer, ByVal value As Order)
    MyBase.Insert(index, value)
  End Sub

End Class

対策後

Dim order As Order = OrderList(i)

短くなったね

まとめ

Genericがない世界なんて嫌いだ。

*1.NET Framework 2.0から使える

2009-10-07

ミューテックスの理解が誤っていた

Mutex::WaitOne(:Integer,:boolean)を実行するたびにシグナルを取りにいこうとするもんだと思っていたけど

実際は、第1引数で指定した時間分*1シグナルを取りに行こうとするという仕様みたいだ

ソース

Public Shared Function Main(ByVal Args() As String) As Integer
  Dim objMutex As Mutex = New Mutex(False, Application.ProductName)
  If (objMutex.WaitOne(24 * 60 * 60 * 1000, False) = False) Then
    MessageBox.Show("待ちきれません!")
    Return -1
  End If
  
  'ここに処理を書く
  
End Function

1日待ってもシグナルが取れなかった場合はメッセージ表示して落ちる

ミューテックスの詳しくはこちら

ミューテックス - Wikipedia

javaでいうとsynchronizedなんだね!知らなかったよ!

スレッドで同じオブジェクトをいじりたい時など、同プロセス内で同期を取りたいなら

SyncLock Me
  '同期中の処理
End SyncLock

という風に書く

まぁ、.NETでスレッドなんて使ったことないんだけども...

*1:ミリ秒

2009-10-05

文字列のバイト単位の操作

VB.NETではバイト単位で文字列の切り取り等が提供されてなく*1

Utilクラス等に書くのでメモ

バイト数取得

Public Shared Function LenB(ByVal text As String) As Integer
  If (text Is Nothing) Then
    Return 0
  Else
    LenB = System.Text.Encoding.Default.GetBytes(text).Length()
  End If
End Function

バイト切り取り(Mid)

Public Shared Function MidB(ByVal str As String, ByVal Start As Integer, ByVal Length As Integer) As String
  If (str Is Nothing) Then
    Return ""
  Else
    Try
      Dim byteArray() As Byte = System.Text.Encoding.Default.GetBytes(str)
      Return System.Text.Encoding.Default.GetString(byteArray, Start, Length)
    Catch ex As ArgumentOutOfRangeException
      MessageBox.Show(ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
      Return ""
    End Try
  End If
End Function
Public Shared Function MidB(ByVal str As String, ByVal Start As Integer) As String
  Return MidB(str, Start, LenB(str) - Start)
End Function

Strings.Midでは第3引数を省略した関数も用意されているので、オーバーロードしてみました。

バイト切り取り(Left)

Public Shared Function LeftB(ByVal str As String, ByVal Length As Integer) As String
  Return MidB(str, 0, Length)
End Function

バイト切り取り(Right)

Public Shared Function RightB(ByVal str As String, ByVal Length As Integer) As String
  Return MidB(str, LenB(str) - Length, Length)
End Function

そういえば

MidBをオーバーロードするときに知ったんだけど

Optionalで引数を宣言してから、メソッド内で省略したかどうかを調べるための関数

IsMissingってあったよなって調べてみたけど、VB6までなんだね

Variant型がなくなったからIsMissing関数もなくなったみたい

VB.NETで実装したければ

  • Object型の引数で宣言して、OptionalでデフォルトNothingを挿入
  • メソッド内で引数がNothingなら、ごにょごにょする

っていう具合で実装できそうだ。

ただ、Nothingが入れられるのがオブジェクトだけだから、

今回のようにInteger型だったりしたら使えない。

別にオーバーロードするしかない。

Optional自体があまり使いたくないけど*2、タイプ量が減るから便利っちゃ便利なんだよね

*1:.NET Framework1.1

*2VB独自の仕様だから

2009-10-02

二重起動メモ

たまには

VB.NETのことも書くよ!

アプリをシリアライズ化したい

同じアプリを起動したときに

最初のプロセスが終わるまで次のプロセスが待機するというプログラム書いた

System.Threading.Mutexを使ってます。

Public Shared Function Main(ByVal Args() As String) As Integer
  Dim objMutex As Mutex = New Mutex(False, Application.ProductName)
  Do While (objMutex.WaitOne(1000, False) = False)
  Loop
  
  'ここに処理を書く
  
End Function

ただし

これだと処理の最中にobjMutexがGCに襲われて二重で起動ができちゃったりする

ビルドがDebugの場合だとGC起きても*1開放されないけど

Releaseだった場合、あっさりと開放されちゃう

開発しているときは大丈夫でも、お客さんにリリースした後に不具合がでるという感じだ

GC対策

Public Shared Function Main(ByVal Args() As String) As Integer
  Dim objMutex As Mutex = New Mutex(False, Application.ProductName)
  Do While (objMutex.WaitOne(1000, False) = False)
  Loop
  
  'ここに処理を書く
  
  GC.KeepAlive(objMutex)
End Function

GCに襲われないためには参照されればいい

GC::KeepAlive(:Object)っていうのがただ単に参照するだけのメソッドで、これを使えば安心

MutexをNewしてからGC.KeepAliveが呼ばれるまでの間、インスタンスが保障される

これでもいいかも

Public Shared Function Main(ByVal Args() As String) As Integer
  Dim objMutex As Mutex = New Mutex(False, Application.ProductName)
  Do While (objMutex.WaitOne(1000, False) = False)
  Loop
  
  'ここに処理を書く
  
  objMutex.ReleaseMutex()
  objMutex.Close()
End Function

いや、むしろこっちの方がいいかも



2009-10-07 理解が誤っていたので追記です

ミューテックスの理解が誤っていた

*1:そもそもGCが起きない?