[2018/03/23] バイト配列を設定バイト値で埋める (No.35)
[2018/03/22] 日付型データの使い方 (No.34)
[2018/03/22] スレッドタイマの使い方(イベント処理に時間が掛かる場合) (No.33)
[2018/03/22] スレッドタイマの使い方 (No.32)
[2018/03/22] 指定した精度の桁数に数値を切り上げ(Math.Ceiling , Math.Floor) (No.31)
-
×
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
-
指定されたバイト配列を、指定値で埋める関数です。バイト配列などは通常は使用しないのですが、制御関係などでは使用することもあり、 初期値として、バイト配列を設定する必要があります。
関数として以下に2種類の関数を例に示していますが、最初は単純に配列1個ずつに指定バイト値を設定しているだけです。 関数にするほどでもないのですが、この関数を元にしてInteger型の配列やDouble型の配列の初期化用の関数をオーバーライドすることができます。
2つ目の関数は、System.Linq の Enumerable を用いた方法を示します。 Enumerable.Repeat で IEnumerable なオブジェクトを生成し、更にそれをArray化して、コピーを行っています。 (こちらの方が、見やすいかもしれません。)バイト配列を設定バイト値で埋める
''' ----------------------------------------------------------------------------- '''
''' バイト配列を設定値で埋める ''' ''' <param name="desArr">対象バイト配列</param> ''' <param name="fillByte">バイト値</param> '''単純に配列を1個ずつ処理 ''' ----------------------------------------------------------------------------- Public Sub FillByteArr(ByVal desArr As Byte(), ByVal fillByte As Byte) If desArr.Length > 0 Then Dim idx As Integer For idx = 0 To desArr.Length - 1 desArr(idx) = fillByte Next End If End Sub ''' ----------------------------------------------------------------------------- '''''' バイト配列を設定値で埋める・その2 ''' ''' <param name="desArr">対象バイト配列</param> ''' <param name="fillByte">バイト値</param> '''System.Linq の Enumerable を用いた方法 ''' ----------------------------------------------------------------------------- Public Sub FillByteArr2(ByVal desArr As Byte(), ByVal fillByte As Byte) If desArr.Length > 0 Then Dim arr As IEnumerable(Of Byte) = Enumerable.Repeat(fillByte, desArr.Length) Array.Copy(arr.ToArray(), desArr, desArr.Length) End If End Sub '***** '仮にテストプログラム '***** Private Sub FillByte_Click(sender As Object, e As EventArgs) Handles FillByte.Click Try '配列宣言 Dim byteArr(10) As Byte '配列を埋める FillByteArr2(byteArr, &H55) '結果の表示 Dim str As String = "" For i = 0 To byteArr.Length - 1 str &= String.Format("byteArr[{0}] = {1}", i, byteArr(i).ToString) & vbCrLf Next MsgBox(str) Catch ex As Exception 'エラー MsgBox(ex.Message) End Try End Sub関連する記事
⇒左詰でバイト配列のコピーを行う
⇒右詰でバイト配列のコピーを行う
PR -
データ型の中で 日付型(Date) はよく使用するもののひとつです。 VB.NETの中に日付のデータ型として、 Date と DateTime がありますが、 どの様な違いがあるのか疑問に思われるでしょうが、以下のMSDNの説明によれば同じものの様です。
⇒データ型の概要 (Visual Basic)
日付型として同じであれば、どちらを使ってもよいのですが、プログラムの中では一つに統一した方が良いと思います。 (私としては DateTime で良いと思います)
以下のソースは抜粋ですが、どちらも同じことをを処理しています。日付型データの使い方1
Dim strWK As String = "" Dim dateWK As Date 'Date型 dateWK = New Date(2018, 1, 20, 15, 17, 30) strWK = dateWK.ToString("yyyy/MM/dd HH:mm:ss") '日付型を書式で変換 Console.WriteLine("dateWK.ToString(""yyyy/MM/dd HH:mm:ss"") = " & strWK) 'DateTime型 Dim dateTimeWK As DateTime dateTimeWK = New DateTime(2018, 1, 20, 15, 17, 30) strWK = dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss") Console.WriteLine("dateTimeWK.ToString(""yyyy/MM/dd HH:mm:ss"") = " & strWK)
ソースの中で、ToString を使用して日付型データから書式に従って文字列に変換しています。 今回の例では書式を"yyyy/MM/dd HH:mm:ss"を使っていますが、通常はこの様な感じで十分かと思います。
尚、この書式には各種ありますが代表的なものを以下に示します。書式指定子 説明 使用例 yy 0埋め下2桁の年 (00 ~ 99) 2009-06-01 13:45:30 ⇒ "09" yyyy 0埋め4桁の年 (0001 ~ 9999) 2009-06-01 13:45:30 ⇒ "2009" M 月 (1 ~ 12) 2009-06-01 13:45:30 ⇒ "6" MM 月:1桁の場合先頭0付 (01 ~ 12) 2009-06-01 13:45:30 ⇒ "06" d 月の日にち (1 ~ 31) 2009-06-01 13:45:30 ⇒ "1" dd 月の日にち:1桁の場合先頭0付 (01 ~ 31) 2009-06-01 13:45:30 ⇒ "01" h 12時間形式の時間 (1 ~ 12) 2009-06-01 13:45:30 ⇒ "1" hh 12時間形式の時間:1桁の場合先頭0付 (01 ~ 12) 2009-06-01 13:45:30 ⇒ "1" H 24時間形式の時間 (0 ~ 23) 2009-06-01 03:45:30 ⇒ "3" HH 24時間形式の時間:1桁の場合先頭0付 (00 ~ 23) 2009-06-01 13:45:30 ⇒ "13" mm 分:1桁の場合先頭0付 (00 ~ 59) 2009-06-01 13:05:30 ⇒ "05" ss 秒:1桁の場合先頭0付 (00 ~ 59) 2009-06-01 13:05:04 ⇒ "04" ddd 曜日の省略名 2009-06-01 13:45:30 ⇒ "月" dddd 曜日の完全名 2009-06-01 13:45:30 ⇒ "月曜日"
次に日付型の計算について説明します。 いろんなところで日付に対して翌日、前日、前月、翌月、前年、翌年などの計算が必要になりますが、 日付型のメソッドとしてそれらを計算できるものが揃っています。 私がよく使うものを以下に示します。
メソッド 書式 説明 AddDays AddDays (value As Double) As DateTime
value 引数は、正または負のどちらの場合もあります指定された日数を加算した新しい DateTime を返します AddMonths AddMonths (value As Integer) As DateTime
value 引数は、正または負のどちらの場合もあります指定された月数を加算した新しい DateTime を返します AddYears AddYears (value As Integer) As DateTime
value 引数は、正または負のどちらの場合もあります指定された年数を加算した新しい DateTime を返します
日付型データの使い方2
'日付の設定 dateTimeWK = New DateTime(2018, 1, 25) '日数+1 dateTimeWK = dateTimeWK.AddDays(1) Console.WriteLine("日数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '日数-1 dateTimeWK = dateTimeWK.AddDays(-1) Console.WriteLine("日数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '月数+1 dateTimeWK = dateTimeWK.AddMonths(1) Console.WriteLine("月数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '月数-1 dateTimeWK = dateTimeWK.AddMonths(-1) Console.WriteLine("月数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '年数+1 dateTimeWK = dateTimeWK.AddYears(1) Console.WriteLine("年数+1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss")) '年数-1 dateTimeWK = dateTimeWK.AddYears(-1) Console.WriteLine("年数-1 = " & dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss"))
上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
dateWK.ToString("yyyy/MM/dd HH:mm:ss") = 2018/01/20 15:17:30 dateTimeWK.ToString("yyyy/MM/dd HH:mm:ss") = 2018/01/20 15:17:30 日数+1 = 2018/01/26 00:00:00 日数-1 = 2018/01/25 00:00:00 月数+1 = 2018/02/25 00:00:00 月数-1 = 2018/01/25 00:00:00 年数+1 = 2019/01/25 00:00:00 年数-1 = 2018/01/25 00:00:00
関連する記事
⇒日付型データの使い方(月末、年末の日付取得)
⇒日付型データの比較について
⇒文字列から数値型への変換(parse - tryparse)
⇒オブジェクト型から数値型への変換(TryParse)
-
スレッドタイマとしては以下のページに説明をしましたが、
⇒スレッドタイマの使い方
この時のタイマイベントの処理は、単に時刻を表示するだけの処理で時間がほとんど掛からないものでした。 しかし、実際のプログラムではそのようなことはなく、処理時間が結構かかるものが在ると思います。
そこで今回は、タイマイベントの処理の中に時間が掛かる処理のシミュレーションとして Sleep関数で3秒待たせることにしました。 この関数の呼び出しの前後で時刻表示を入れましたので、何時の時点での処理なのかが分かります。 実際のプログラムは以下の様になります。スレッドタイマの使い方(イベント処理が時間が掛かる場合)
Imports System.Threading Imports System.Threading.Thread Public Class frmTimerTh2 'スレッドタイマ Private mtimer As System.Threading.Timer '''
''' フォームロードイベント ''' Private Sub frmTimerThMul_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) 'テキストボックスに現在時刻表示 Dim strDate As String = Now.ToString("hh:mm:ss") Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate}) '3秒の待ち Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒Start...5秒の待ち"}) Sleep(3 * 1000) Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒End.....5秒の待ち"}) End Sub ' ラベル表示デリゲート宣言 Private Delegate Sub SetLabelTextDelegate(ByVal strText As String) '''''' ラベル表示 ''' ''' <param name="strText">表示文字列</param> Private Sub SetLabelText(ByVal strText As String) Me.TextBox1.Text &= strText & vbCrLf End Sub End Classこれを実行させると、以下の様な表示なります。
最初、4個の「Start...5秒の待ち」が続いてその後で「End.....5秒の待ち」が追いかけてくる感じになっています。
これは、スレッドタイマのイベントの時間間隔が1秒なので、先に発生したイベントを待たずに 次々とイベントが発生させていることが分かります。 つまり、各1秒毎のイベントは別々に実行していることになります。
3秒待ちの処理が重なって処理されては困ることになります。 そこで、以下のプログラムの様に、イベント処理中のフラグを用いて処理をスキップさせてみました。スレッドタイマの使い方(イベント処理が時間が掛かる場合:改良版)
Imports System.Threading Imports System.Threading.Thread Public Class frmTimerTh2 'スレッドタイマ Private mtimer As System.Threading.Timer '''
''' フォームロードイベント ''' Private Sub frmTimerThMul_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) '処理中フラグ Static fProcessing As Boolean = False '処理中フラグOFFのチェック If fProcessing = False Then '処理中フラグON fProcessing = True 'テキストボックスに現在時刻表示 Dim strDate As String = Now.ToString("hh:mm:ss") Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate}) '3秒の待ち Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒Start...5秒の待ち"}) Sleep(3 * 1000) Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate & "⇒End.....5秒の待ち"}) '処理中フラグOFF fProcessing = False End If End Sub ' ラベル表示デリゲート宣言 Private Delegate Sub SetLabelTextDelegate(ByVal strText As String) '''''' ラベル表示 ''' ''' <param name="strText">表示文字列</param> Private Sub SetLabelText(ByVal strText As String) Me.TextBox1.Text &= strText & vbCrLf End Sub End Classこのプログラムの実行は以下の様になり、イベントの多重呼び出しは無くなりました。
関連する記事
⇒Timerコントロールの使い方 :[Timer,Interval]
⇒複数のTimerコントロールの使い方 :[Timer,Interval]
⇒スレッドタイマの使い方 :[System.Threading.Timer,Delegate,Invoke]
-
標準コントロールのTimerコントロールとは異なり、タイマイベントを別スレッドとして起動する スレッドタイマの説明をします。
タイマイベントはSystem.Threadingの名前空間にある、Timerクラスで、 MSDNの説明によれば以下の様になっています。System.Threading.Timer について
<コンストラクター(複数ある中の今回利用のもの)> Public Sub New ( callback As TimerCallback, :TimerCallback を実行するメソッドを表すデリゲート state As Object, :コールバック メソッドで使用される情報を格納したオブジェクト または nullです。 dueTime As Integer, :前に遅延する時間 callback ミリ秒単位で呼び出されます。 指定 Timeout.Infinite 、タイマーが起動しないようにします。 0 を指定して、タイマーをすぐに開始します。 period As Integer :呼び出しの間の時間間隔 callback, 、(ミリ秒単位)。 指定 Timeout.Infinite 周期的なシグナル通知を無効にします。 ) <メソッド> Public Function Change ( dueTime As Integer, :時に指定されたコールバック メソッドを呼び出す前に遅延する時間数、 Timer (ミリ秒単位) が作成されました。 指定 Timeout.Infinite タイマーが再開されないようにします。 0 を指定して、タイマーをすぐに再開します。 period As Integer :コールバック メソッドの呼び出し間の時間間隔が指定されたときに、 Timer (ミリ秒単位) が作成されました。 指定 Timeout.Infinite 周期的なシグナル通知を無効にします。 ) As Boolean 戻り値:true の場合は、タイマーが正常に更新されました。それ以外の場合、 falseです。
今回の例としては、スレッドタイマを作成し、 TimerCallback イベントで 出力ウインドウに現在の時刻を表示する動作をします。
スレッドタイマの使い方
Imports System.Threading Imports System.Threading.Thread Public Class frmTimer2 'スレッドタイマ Private mtimer As System.Threading.Timer '''
''' フォームロードイベント ''' Private Sub frmTimer2_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) '出力ウインドウに時刻表示 Console.WriteLine("Time:" & Now.ToString("hh:mm:ss")) End Sub End Class実行させると、1000msec毎に関数 TimerEvent が呼び出され、出力ウインドウに現在の時刻が表示されることが確認できます。
そこで、以下のページの様にフォーム上のラベルに時刻を表示させてみようとしました。
・Timerコントロールの使い方 :[Timer,Interval]
しかし、思っていた様にラベルには時刻が表示されずに、以下の様なエラーが表示されてしまいました。
「有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'Label1' がアクセスされました。」スレッドタイマの使い方・エラーの出たフォーム上のラベルへの表示
Imports System.Threading Imports System.Threading.Thread Public Class frmTimer2 'スレッドタイマ Private mtimer As System.Threading.Timer '''
''' フォームロードイベント ''' Private Sub frmTimer2_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) ''出力ウインドウに時刻表示 'Console.WriteLine("Time:" & Now.ToString("hh:mm:ss")) 'ラベルに現在時刻表示 Me.Label1.Text = Now.ToString("hh:mm:ss") End Sub End Class「有効ではないスレッド間の操作...」エラーは、 あるスレッドから異なるスレッド上のコントロールにアクセスしようとしたことが原因でした。
つまりスレッドタイマの TimerEvent と、 フォームのラベルがあるスレッドとは全く別にスレッド上で動作していることになります。
これを解決するには、デリゲートという方法を使います。 デリゲートを使えば別スレッドからフォーム上のコントロールへのアクセスが出来る様になります。
デリゲートの方法ですがソースを見た方が分かりやすいと思いますので、 以下に示します。スレッドタイマの使い方・デリゲート宣言とInvoke関数
Imports System.Threading Imports System.Threading.Thread Public Class frmTimer2 'スレッドタイマ Private mtimer As System.Threading.Timer '''
''' フォームロードイベント ''' Private Sub frmTimer2_Load(sender As Object, e As EventArgs) Handles Me.Load 'タイマコールバック関数 Dim timerDelegate As TimerCallback = New TimerCallback(AddressOf TimerEvent) '1000msecタイマ生成(コールバック関数の設定) mtimer = New Timer(timerDelegate, Nothing, 0, 1000) End Sub '''''' System.Threading.Timer からの呼び出しを処理するメソッド ''' ''' <param name="state">このデリゲートで呼び出されたメソッドに関連するアプリケーション固有の情報を格納するオブジェクト</param> Private Sub TimerEvent(ByVal state As Object) 'ラベルに現在時刻表示 Dim strDate As String = Now.ToString("hh:mm:ss") Invoke(New SetLabelTextDelegate(AddressOf SetLabelText), New Object() {strDate}) End Sub ' ラベル表示デリゲート宣言 Private Delegate Sub SetLabelTextDelegate(ByVal strText As String) '''''' ラベル表示 ''' ''' <param name="strText">表示文字列</param> Private Sub SetLabelText(ByVal strText As String) Label1.Text = strText End Sub End Classこのソースで一応ラベルへの表示は以下図の様に出来る様になりました。
ここで重要なのは、ラベル表示用関数のデリゲート宣言と Invoke 関数です。 デリゲート(delegate)という動詞には「委譲する、委任する」、名詞では「代理人」などという意味が有りますが、 デリゲートの呼び出し側からすれば、関数の引数を合わせて、デリゲートされたものを Invoke 関数で 呼出すだけのことになります。
デリゲートとして宣言されている中身の処理については、そちらままかせの感じになります。
デリゲートとして宣言された関数を Invoke で呼出してやれば、スレッドを超えて処理が可能になります。 さて、 Invoke ですがMSDNの説明では以下の様になっています。Public Function Invoke(method As System.Delegate, ParamArray args() As Object) As Object 概要: コントロールの基になるウィンドウ ハンドルを所有するスレッド上で、 指定した引数リストを使用して、指定したデリゲートを実行します。 パラメーター: method: args パラメーターに指定されている数および型と同じ数 および型のパラメーターをとるメソッドへのデリゲート。 args: 指定したメソッドに引数として渡すオブジェクトの配列。 メソッドが引数をとらない場合、このパラメーターは null になります。
関連する記事
⇒Timerコントロールの使い方 :[Timer,Interval]
⇒複数のTimerコントロールの使い方 :[Timer,Interval]
⇒スレッドタイマの使い方(イベント処理が時間が掛かる場合):[System.Threading.Timer,Delegate,Invoke]
-
指定された Decimal Double Long の値を、指定された桁で切り上げる関数です。
指定された値が正の場合は、有効桁数の10の累乗で割ったものを Math.Ceiling 関数で最小の整数値を求めて、更に累乗値を掛けてやります。 指定された値が負の場合は、有効桁数の10の累乗で割ったものを Math.Floor 関数で最小の整数値を求めて、更に累乗値を掛けてやります
テストプログラムの動作としては、Double値が「123456」で有効桁数が「3」の場合、「123456」を先ず「1000」で割って「123.456」となり Math.Ceiling 関数を通すことで「124.000」となり結果「124000」が関数戻り値となります。指定した精度の桁数に数値を切り上げ
''' ------------------------------------------------------------------------ '''
''' 指定した精度の数値の切り上げ ''' ''' <param name="Val">丸め対象の倍精度浮動小数点数</param> ''' <param name="intUnit">戻り値の有効桁数の精度</param> '''切り上げられた数値。 ''' ------------------------------------------------------------------------ Public Function RoundUp(ByVal Val As Double, ByVal intUnit As Integer) As Double Dim dblPow As Double = System.Math.Pow(10, intUnit) If Val > 0 Then 'Ceiling:指定した倍精度浮動小数点数以上の数のうち、最小の整数値を返します Return System.Math.Ceiling(Val / dblPow) * dblPow Else 'Floor:指定した倍精度浮動小数点数以下の数のうち、最大の整数を返します Return System.Math.Floor(Val / dblPow) * dblPow End If End Function Public Function RoundUp(ByVal Val As Decimal, ByVal intUnit As Integer) As Decimal Dim decPow As Decimal = System.Math.Pow(10, intUnit) If Val > 0 Then 'Ceiling:指定した 10 進数以上の数のうち、最小の整数値を返します Return System.Math.Ceiling(Val / decPow) * decPow Else 'Floor:指定した 10 進数以下の数のうち、最大の整数を返します Return System.Math.Floor(Val / decPow) * decPow End If End Function Public Function RoundUp(ByVal Val As Long, ByVal intUnit As Integer) As Long Dim decPow As Decimal = System.Math.Pow(10, intUnit) If Val > 0 Then Return System.Math.Ceiling(Val / decPow) * decPow Else Return System.Math.Floor(Val / decPow) * decPow End If End Function '***** '仮にテストプログラム '***** Private Sub BtnRoundUp_Click(sender As Object, e As EventArgs) Handles BtnRoundUp.Click Try Dim decData As Decimal = 123456 Dim dblData As Double = 123456 Dim lngData As Long = 123456 Dim str As String = "" str &= "Decimal:" & decData.ToString & "-->" & RoundUp(decData, 3).ToString & vbCrLf str &= "Double :" & dblData.ToString & "-->" & RoundUp(dblData, 2).ToString & vbCrLf str &= "Long :" & lngData.ToString & "-->" & RoundUp(decData, 1).ToString & vbCrLf decData = -decData dblData = -dblData lngData = -lngData str &= "Decimal:" & decData.ToString & "-->" & RoundUp(decData, 3).ToString & vbCrLf str &= "Double :" & dblData.ToString & "-->" & RoundUp(dblData, 2).ToString & vbCrLf str &= "Long :" & lngData.ToString & "-->" & RoundUp(decData, 1).ToString & vbCrLf MsgBox(str) Catch ex As Exception 'エラー MsgBox(ex.Message) End Try End Sub