読者です 読者をやめる 読者になる 読者になる

checkpoint

自分用のIT技術系ノートです。古い情報や間違っている情報もあるので注意。

リフレクションを使って(ジェネリック)メソッドを実行する

C# .NET
public static string TestMethod1(int input)
{
  return "Test Method 1 : " + input.ToString();
}

がProgramクラスにある場合、これをリフレクションを使って呼び出すには

var mi = typeof(Program).GetMethod("TestMethod1");
var ret = mi.Invoke(null, new object[] { 100 });

のように名前を文字列で指定してMethodInfoを取得後、Invokeします。
名前を文字列で指定しない場合、

var f = new Func<int, string>(Program.TestMethod1);
var ret = f.Method.Invoke(null, new object[] { 99 });

のように、一旦デリゲートにしてそのMethodプロパティからInvokeします。

ジェネリックの場合は、

public static string TestMethod2<T>(T input)
{
  return "Test Method 2 : " + input.ToString();
}

こんな感じのメソッドがあったとして、名前を文字列で指定してInvokeする方法は

var mi = typeof(Program).GetMethod("TestMethod2");
var gmi = mi.MakeGenericMethod(typeof(double));
var ret = gmi.Invoke(null, new object[] { 99.999d });

名前を文字列で指定せずにデリゲートから呼び出す際は少し面倒ですが、

// 型パラメータを適当に指定する
var f = new Func<int, string>(Program.TestMethod2<int>);
var gmd = f.Method.GetGenericMethodDefinition();
var gmi = gmd.MakeGenericMethod(typeof(double));
var ret = gmi.Invoke(null, new object[] { 99.999d });

のようにすればOK。

メソッドをそのまま呼び出せるのに、なぜわざわざリフレクションで呼ぶのか?
ジェネリックの型パラメータをTypeオブジェクトで指定したいからです。

(例えばひとつ上のコードでは、double型のTypeオブジェクトがあれば型パラメータにdoubleを指定して呼び出せるわけです。)

ClickOnceで起動時エラー

.NET

(問題)

このアプリケーションをインストールまたは実行できません。このアプリケーションでは、まずグロバール アセンブリ キャッシュ(GAC)にアセンブリ <名前・バージョン> をインストールする必要があります。このアプリケーションをインストールまたは実行できません。このアプリケーションでは、まずグロバール アセンブリ キャッシュ(GAC)にアセンブリ <名前・バージョン> をインストールする必要があります。

 

(解決)

プロジェクトのプロパティ→「アプリケーション ファイル」から対象のファイルを「含む」にして配布

datetime型から時刻を取り去るいくつかの方法

SQL Server

SQL Serverでdatetimeから時刻を除去したいときってありますよね。
「本日0時時点のdatetime型データが必要!」というような場合、どのような方法が考えられるでしょうか。

SQL Server 2008の場合

SQL Server 2008では「date型」「time型」という新しいデータ型が用意されています。
従って、時刻を除去したい場合は単純にdate型に変換すればよろしい。

CONVERT(date, GETDATE())

SQL Server 2000、2005の場合

これらのバージョンにはdate型は無いので、一旦文字列に変換してからdatetimeに再変換するのがセオリー。

CONVERT(datetime, CONVERT(char(8), GETDATE(), 112))


が、世の中には私のようにどうしてもdatetimeを文字列に変換したくない!(理由は無いけど)気持ち悪い!という人も居ると思いますので、datetime型のまま時刻を取り去る方法も考えてみました。

DECLARE @today AS datetime
SET @today = GETDATE()
SET @today = DATEADD(hour, DATEPART(hour, @today)*-1, @today)
SET @today = DATEADD(minute, DATEPART(minute, @today)*-1, @today)
SET @today = DATEADD(second, DATEPART(second, @today)*-1, @today)
SET @today = DATEADD(millisecond, DATEPART(millisecond, @today)*-1, @today)
SELECT @today

まあ、単純に時刻を引いて0時にしているだけですが、これならdatetime型のまま時刻を取り去れます。
上の例では段階を踏んでいますが、もちろん一行で書くこともできます。
ワケわからなくなるからお勧めしませんけど。
あと、ミリ秒を引くのを忘れると中途半端な時刻になっちゃうので注意。


あと、文字列に変換したくないけど数値に変換するのは許せるという人は以下のような手段も使えます。

DECLARE @today AS datetime
SET @today = GETDATE()
DECLARE @tmp AS decimal(19, 12)
SET @tmp = CONVERT(decimal(19, 12), @today)
SET @today = CONVERT(datetime, ROUND(@tmp, 0, 1))
SELECT @today

datetime型は実数に変換すると、整数部が日付(最大7桁)、小数部が時刻(最大12桁)になります。
なので、実数から少数以下を切り捨ててからもう一度datetimeに変換すれば大丈夫です。
ROUND関数のオプションを間違えて四捨五入しないように注意。

SELECT INTO句で一時テーブルを作成した場合、SELECTに空文字を指定しているとエラー(2000まで)

SQL Server

最近遭遇した謎なエラー。

SELECT '' AS col1 INTO #T


このようなSQLを実行すると、以下のようなエラーになりました。

列 'col1' の幅が無効です: 0。

ところが同じSQLを別のSQLサーバー(2005)で動作させると正常終了。

どうやらSQL Serverのバージョンが2000までだと上記のエラーになるようです。

ちなみに2000で上のようなSQLを実行した場合は、

SELECT CAST('' AS nvarchar(1)) AS col1 INTO #T

のように型と文字長の指定をしてあげると大丈夫です。
まあ、エラーにならなくても型の指定はちゃんとしておけってことですね。

(v1.1対象)StringDictinaryのKeyは強制的に小文字にされる

.NET

久しぶりにメモっぽくブログ更新。



StringDictionary クラス (System.Collections.Specialized)

は、Stringを扱うDictionaryが必要な場合に使ったりします。



Hashtable クラス (System.Collections)

と違ってキャストが必要ないから。

しかしここで思わぬ落とし穴が…

MSDNのStringDictionaryの説明から引用

キーは大文字と小文字を区別せずに処理されます。つまり、キーは文字列ディクショナリで使用される前に小文字に変換されます。

「大文字と小文字を区別しないこと」と「小文字に強制変換すること」は違うと思うのですが…

とにかくStringDictinaryのキーは小文字に強制変換されます。

HashTableではそういうことはないので、小文字にされて困る場合はHashTableを使いましょう。

まあ、v2.0以上の場合は

Dictionary ジェネリック クラス (System.Collections.Generic)

使えばいいってことです。

どっとはらい

FakeWorkTable

SQL Server .NET


SQL Server 2005 + .NETアプリでこんなエラーが出たよ!

System.Data.SqlClient.SqlException: 4 バイトの内部オーバーヘッドを含めて、最小行サイズが 16017 になるので、テーブル 'FakeWorkTable' を作成または変更できませんでした。このサイズは、テーブル行の最大許容サイズの 8094 バイトを超えています。


「FakeWorkTable」でググって見てもMicrosoftのヘルプはおろか、日本語のサイトが見つからない有様。
探せど探せど英語のフォーラムばっかり。しかも頑張って読んでも大したこと書いていなかったり。

よく調べてみるとSqlParameterオブジェクトのSizeプロパティを明示的に指定しない状態で、空文字を値に放り込んだ場合に限り発生している模様。(型指定はChar)

理由はよくわからないけど、Sizeを実際のDB上の文字長に指定すれば解決しました。
ここんとこ詳しく説明してくれる人いないかな。

FlagsAttributeの使い方

.NET C#

列挙型にFlagsAttributeを付与すると、フラグとして扱うことができます。

[Flags] 
public enum Color 
{ 
 Red = 1, 
 Blue = 2, 
 Yellow = 4, 
 Green = 8, 
} 

値は2の累乗になるようにします。

フラグの立て方。(RedおよびBlue)

Color c = Color.Red | Color.Blue;

特定のフラグが立っていることを確認する場合。(Redが立っているか?)

if ((c & Color.Red) == Color.Red){ .... }