佐々木屋

技術的なことから趣味まで色々書きます

型変換は何を使う?(値型⇒String型)

今回は値型⇔string型です。共通認識は前回と一緒です。

  • VB.NETのCIntやCDecなどのC●●●は、CTypeと等価なので全てCTypeで検証
  • 時間計測はSystem.Diagnostics名前空間のStopwatchクラスを利用
  • 繰り返し回数maxは10,000,000回とする
  • それぞれを3回ずつ実行する

今回考えられる候補は、ToString、Convert、CType(VB.NETのみ)です。

int a = 123456789;

sw.Start();
for (int i = 1; i <= max; ++i) {
    string res = a.ToString();
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString() + " ToString");
sw.Reset();

sw.Start();
for (int i = 1; i <= max; ++i) {
    string res = Convert.ToString(a);
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString() + " Convert");
sw.Reset();
Dim a As Integer = 123456789

sw.Start()
For i As Integer = 1 To max
    Dim res As String = a.ToString()
Next
sw.Stop()
Console.WriteLine(sw.Elapsed.ToString() & " ToString")
sw.Reset()

sw.Start()
For i As Integer = 1 To max
    Dim res As String = Convert.ToString(a)
Next
sw.Stop()
Console.WriteLine(sw.Elapsed.ToString() & " Convert")
sw.Reset()

sw.Start()
For i As Integer = 1 To max
    Dim res As String = CType(a, String)
Next
sw.Stop()
Console.WriteLine(sw.Elapsed.ToString() & " CType")
sw.Reset()

結果は以下の通りです。

項目 C#
VB.NET
ToString 00:00:02.7788599
00:00:01.9934980
00:00:02.7895854
00:00:02.7907917
00:00:02.6248466
00:00:02.7211354
Convert 00:00:02.1907958
00:00:01.9914505
00:00:02.0879492
00:00:02.0197473
00:00:02.1154300
00:00:02.1438101
CType × 00:00:01.5729566
00:00:02.0180006
00:00:02.0006762


以外な結果です。最速はVB.NETのCType(CStr)でした。それでもToString、Convertと大差ありません。ToStringは書式指定が可能ですね。あとはnullの処理をどうするかだけかと思います。

結論、値型⇒String型はToStringで良いでしょう。

管理者権限で自動実行されない・・・

Windows7と8(8.1)まではスタートアップから問題なく管理者権限で実行できていましたが、Windows10からは実行不可となっています。

UACを解除したり、アプリケーションマニフェストを変更したりしてみましたが効果ナシ・・・。ということで、別の方法を模索していましたが、サービスにするほどでもない(起動時一発で済むような)ので、最終的にタスクスケジューラにする方法で落着。

タスクスケジューラを作成します。
f:id:sasaki816:20191222221610j:plain:w600

作成画面の「トリガー」で、「ログオン時」を選択します。
f:id:sasaki816:20191222221615j:plain

あとはお好みで設定します。
マニフェストを変更しないのであれば、管理者権限で実行するにチェックするのをお忘れなく。


UACについてはこちら。
sasaki816.hatenablog.com


マニフェスト変更についてはこちら。
sasaki816.hatenablog.com

トリガーの登録

SQLServerのトリガ登録の覚えです。

INSERTやUPDATEなどでテーブルに変更があった場合に処理を呼ぶ場合に使用するのが「トリガ」です。
トリガーはテーブルごとの設定になります。

CREATE TRIGGER [ トリガー名 ] ON [ 対象テーブル名 ] 
AFTER { [ INSERT] , [ UPDATE ] , [ DELETE ] } 
AS BEGIN
[ 実行したいスクリプト ]
END



例えば、テーブル更新時に登録日時を自動で入れたい場合などは以下のようにします。

CREATE TRIGGER [dbo].[trig_取引明細TEST] ON  [dbo].[取引明細TEST] 
AFTER INSERT
AS BEGIN
UPDATE 取引明細EST SET 売上日 = getdate() WHERE 支店CD = (SELECT 支店CD FROM inserted)
END



insertedテーブルにはINSERT、UPDATEステートメント実行で影響を受けた行がコピーされます。そこのキーを指定すれば実テーブルを更新できる、というわけです。

従って、insertedテーブルにキーが無く、更新テーブルにもキーが無い場合は、実テーブルの意図しない行が更新されてしまう可能性がありますので注意が必要ということになります。


なお、設定はテーブルの「トリガー」に格納されます。
f:id:sasaki816:20191002131330j:plain

iPhoneのSafari対応 Javascriptからクリップボードを設定

会社の携帯が変更になるようで、それに伴い色々設定作業が入ります。
設定作業自体は部下にやらせるとして、やはり不用意にパスワードなどは見せたくないわけで。

そうした時にパスワードをクリップボード経由で扱えばいいのですが、iPhoneクリップボードはちょっと特殊のようで。


仕組みとして直接クリップボードへ値を入れることは出来ません。但し、テキストボックスの値を疑似的に選択状態にすればクリップボードへの代入が可能になります。

手順としては、ダミーのテキストボックス(txtPassword )を1個用意してそこに値を設定し、その値を選択状態にしてクリップボードへコピーします。

var txtPassword = document.getElementById("txtPassword ");
var range = document.createRange();
range.selectNode(txtPassword);
window.getSelection().addRange(range);
document.execCommand('copy');
alert("コピーしました。");

後は空文字をテキストボックスに代入し、それを同様の処理でクリップボードへコピーすればクリップボード内を削除出来ます。ダミーのテキストボックスの値は文字色を白にしたり、コピー直後にリダイレクトして値を削除したり、色々工夫すれば担当者レベルであれば目くらましになるでしょう。

MIMETypeの取得

ファイルのMIMETypeを取得する方法です。
.NET Frameworkによって違いますので、お好みの方をどうぞ。

.NET Framework4.5以上

System.Web.MimeMappingクラスのGetMimeMappingメソッドへファイル名を渡すことで取得可能です。

System.Web.MimeMapping.GetMimeMapping("ファイル名");
System.Web.MimeMapping.GetMimeMapping("ファイル名")

GetMimeMappingメソッドはフルパスを渡しても結果的には一緒です。恐らく内部的にファイル拡張子から判断しているものと思われます。

ですので、拡張子が認識できない場合はnullを返さずoctet-streamを返します。ファイル有無も関係ありません。

.NET Framework4.0まで

System.Web.MimeMappingクラスでは取得できませんので、ClassRootレジストリからContent Typeを取得する方法となります。

使い方によりけりですが、GetMimeMappingメソッドと合わせるならデフォルトでoctet-streamを返す仕様が好ましいです。あえてnullを返すという手もありますが、臨機応変にどうぞ。

private string GetMimeTypeByExtension(string ext) {
    string res = "application/octet-stream";

    try {
        var key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
        if (key != null) {
            var mimetype = key.GetValue("Content Type");
            if (mimetype != null) res = (string)mimetype;
        }
    }
    catch { }

    return res;
}
Private Function GetMimeTypeByExtension(ByVal ext As String) As String
    Dim res As String = "application/octet-stream"

    Try
        Dim key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext)
        If key IsNot Nothing Then
            Dim mimetype = key.GetValue("Content Type")
            If mimetype IsNot Nothing Then res = CType(mimetype, String)
        End If
    Catch ex As Exception
    End Try

    Return res
End Function



if文のところはnull条件演算子を使えばもっと簡潔に書けますが、.NET Framework4.0では使用できないので少し読みにくいですが仕方ありません。

IIS管理外の画像ファイルを表示させる

ASP.NETIIS管理内のディレクトリの画像ファイルをImageコントロールへ動的に表示させるためには以下で簡単にできますが、

img.ImageUrl = ”相対パス”

これを別のディレクトリに置いた画像を表示させたい場合は、System.Drawing名前空間のBitmapクラスへインスタンス展開して、それをResponse.OutputStreamに向けて保存すれば表示可能です。

protected void Page_Load(object sender, EventArgs e) {
    Bitmap bmp = new Bitmap(@"pngファイル絶対パス");
    Response.ContentType = "image/png";
    Response.Flush();
    bmp.Save(Response.OutputStream, ImageFormat.Png);
    Response.End();
}
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim bmp As New Bitmap("pngファイル絶対パス")
    Response.ContentType = "image/png"
    Response.Flush()
    bmp.Save(Response.OutputStream, ImageFormat.Png)
    Response.End()
End Sub



で、ここまでは簡単な話ですが、ここで終わってはツマランわけで。
折角なので、MIMEや保存時のImageFormatを表示させるファイルの拡張子によって動的に設定できれば汎用性はぐっとあがります。

最初の1行目のビットマップクラスへの展開によって、ContentTypeとImageFormatを動的に取得するようなクラスを考えればよいことになります。

オブジェクト指向の観点を考慮しつつ構築する演習としてみましょう。
ということで、このまま演習へ引き継ぎます。

Strategyパターン

デザインパターンの中でもよく見るものですが、オブジェクト指向が分からないとチンプンカンプンになります。

「戦略」という意味で、メソッドなどにちりばめられた静的なアルゴリズムを、ifやswitch(VB.NETはSelect Case)などの条件分岐で動的にアルゴリズムごと変更してしまう手法です。

このパターンによってアルゴリズムに変更が生じても、条件分岐で意図しない影響が出ないようになります。

まずインターフェースを定義します。

Public interface IStrategy {
    void Work();
}
Public Interface IStrategy
    Sub Walk()
End Interface

次にそれらを実装したクラスを定義します。

public class Class1 : IStrategy {
    public void Walk() {
        Console.WriteLine("テクテク歩く");
    }
}

public class Class2 : IStrategy {
    public void Walk() {
        Console.WriteLine("ノソノソ歩く");
    }
}

public class Class3 : IStrategy {
    public void Walk() {
        Console.WriteLine("サクサク歩く");
    }
}
Public Class Class1
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("テクテク歩く")
    End Sub
End Class

Public Class Class2
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("ノソノソ歩く")
    End Sub
End Class

Public Class Class3
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("サクサク歩く")
    End Sub
End Class

最後に、インターフェースを通じて必要な処理を返す(委譲させるための)クラスを定義します。

public class Context {
    public IStrategy Strategy { set; get; }

    public void Execute() {
        Strategy.Walk();
    }
}
Public Class Context
    Public Property Strategy As IStrategy

    Public Sub Execute()
        Strategy.Walk()
    End Sub
End Class

こうすることで、メインコードからアルゴリズムを切り離してカプセル化することにより、不用意な操作やアルゴリズムの変更に対応できるようになります。

var cls = new Context();

cls.Strategy = new Class1();
cls.Execute();

cls.Strategy = new Class2();
cls.Execute();

cls.Strategy = new Class3();
cls.Execute();
Dim cls As New Context()

cls.Strategy = New Class1()
cls.Execute()

cls.Strategy = New Class2()
cls.Execute()

cls.Strategy = New Class3()
cls.Execute()