C# 可変引数の罠
◆ 引数に params キーワードを設定すると可変引数
◆ 可変引数に型情報のない null を渡すと可変引数の配列自体が null になる
◆ リフレクションから呼び出すときは可変引数の部分はさらに配列にしないとだめ
◆ 可変引数に型情報のない null を渡すと可変引数の配列自体が null になる
◆ リフレクションから呼び出すときは可変引数の部分はさらに配列にしないとだめ
C# では params キーワードを使って可変引数を使えます
こういう関数を作って
適当に呼び出してみます
引数 vals は object の配列で引数に渡した数の長さがあります
シンプルでいいね と思ったところで例外が
ん!???
1 つ null が入った配列になると思ったら引数の vals 自体が null になっています
null だけを引数に渡すと 配列であるはずの引数自体が null として受け取れるみたい
可変引数なんだから null にしてほしいことなんてなくない?
むしろバグのもとだよ っていいたいです
実際これが原因でバグが起きてましたし……
配列形式で null を渡したいときにはこうすればよいらしいです
null が 2 つ以上のときは気にしなくても大丈夫
Reflection から呼び出すときは可変部分を2重に配列にする必要があります
params キーワードは最後の引数に書く必要がありますが params キーワードの前に可変でない引数を受け取ることができます
その場合のリフレクション呼び出しはこうなります
static void fn(params object[] vals)
{
Console.WriteLine($"{vals?.Length}, {vals}");
}
{
Console.WriteLine($"{vals?.Length}, {vals}");
}
こういう関数を作って
適当に呼び出してみます
fn(1,2,3);
fn("a", false);
fn(1, "b", null);
fn(null, 3);
fn(DateTime.Now);
fn();
fn("a", false);
fn(1, "b", null);
fn(null, 3);
fn(DateTime.Now);
fn();
3, System.Object[]
2, System.Object[]
3, System.Object[]
2, System.Object[]
1, System.Object[]
0, System.Object[]
2, System.Object[]
3, System.Object[]
2, System.Object[]
1, System.Object[]
0, System.Object[]
引数 vals は object の配列で引数に渡した数の長さがあります
シンプルでいいね と思ったところで例外が
fn(null);
,
ん!???
1 つ null が入った配列になると思ったら引数の vals 自体が null になっています
null だけを引数に渡すと 配列であるはずの引数自体が null として受け取れるみたい
可変引数なんだから null にしてほしいことなんてなくない?
むしろバグのもとだよ っていいたいです
実際これが原因でバグが起きてましたし……
配列形式で null を渡したいときにはこうすればよいらしいです
fn((object)null);
1, System.Object[]
直接 null を渡すのがダメで一度変数に入れるなど型情報を持っていれば可変引数のひとつとして扱われるようですnull が 2 つ以上のときは気にしなくても大丈夫
fn(null, null);
fn((object)null, (object)null);
fn((object)null, (object)null);
2, System.Object[]
2, System.Object[]
2, System.Object[]
Reflection から呼び出すときは可変部分を2重に配列にする必要があります
var type = MethodBase.GetCurrentMethod().DeclaringType;
var method = type.GetMethod("fn");
var invoke_param = new object[]
{
new object[] {"a", "b"}
};
method.Invoke(null, invoke_param);
var method = type.GetMethod("fn");
var invoke_param = new object[]
{
new object[] {"a", "b"}
};
method.Invoke(null, invoke_param);
2, System.Object[]
params キーワードは最後の引数に書く必要がありますが params キーワードの前に可変でない引数を受け取ることができます
その場合のリフレクション呼び出しはこうなります
static public void fn2(int a, string b, params object[] vals)
{
Console.WriteLine($"{vals?.Length}, {vals}");
}
{
Console.WriteLine($"{vals?.Length}, {vals}");
}
var type = MethodBase.GetCurrentMethod().DeclaringType;
var method = type.GetMethod("fn2");
var invoke_param = new object[]
{
1,
"a",
new object[] {"a", "b"}
};
method.Invoke(null, invoke_param);
var method = type.GetMethod("fn2");
var invoke_param = new object[]
{
1,
"a",
new object[] {"a", "b"}
};
method.Invoke(null, invoke_param);
2, System.Object[]