PowerShellの柔軟過ぎる型変換・キャストに恐れおののく

stuncloudさんがこんな記事を。
PowerShellは暗黙の型変換がんばりすぎ | たっぷす庵
そうそう、PowerShellのキャストって高機能過ぎですよね。
気になったので、その高機能っぷりを調べてみた。

結果

高機能なキャストの動作ですけど、、、

  1. 対象がstringなら、stringを引数にとるParseメソッド(TryParseは使わない)
  2. 対応する型のコンストラクター
  3. 対応する型のキャスト
  4. どれも失敗なら、stringに変換(多分ToString)してもう一回

ということの模様。
、、、キャスト、最後かよ。
Parseメソッドはstring限定っぽい。



型変換

リテラル

  • ''または""ならstring(""は置換が入る)
  • 数字のみならint
  • 小数点が入ったらdouble(decimalじゃないのか、、、)

という解釈で、変数の場合はその型で、上記の高機能なキャストが行われるみたい。

New-Objectだと

  1. 配列は一旦ばらばらにして、それぞれを引数とみなす
  2. 対応する型のコンストラクター

を呼ぶみたい。

スクリプト

確認のため作成したものです。
int二つのコンストラクターと、int配列のコンストラクター
int二つの配列をキャストするとint配列のコンストラクターが呼ばれるけど、New-Objectするとint二つのが呼ばれる。
int三つの配列をキャストするとint配列のコンストラクターが呼ばれるけど、New-Objectはエラーになる。
ただ、数値二つのオブジェクト配列をキャストするとエラーになるけど、New-Objectはint二つのが呼ばれる。
うーん、統一性が、、、。

$src = @"
using System;

public class Test
{
	public string StringArg { get; set; }
	public int IntArg { get; set; }

	public Test()
	{
		Console.WriteLine("Constructor Default");
	}

	public Test(string arg)
	{
		Console.Write("Constructor string ");
		Console.WriteLine(arg);
		StringArg = arg;
	}

	public Test(int arg)
	{
		Console.Write("Constructor int ");
		Console.WriteLine(arg);
		IntArg = arg;
		StringArg = IntArg.ToString();
	}

	public Test(double arg)
	{
		Console.Write("Constructor double ");
		Console.WriteLine(arg);
		StringArg = arg.ToString();
	}

	public Test(int arg1, int arg2)
	{
		Console.Write("Constructor int2 ");
		Console.Write(arg1);
		Console.Write(" ");
		Console.WriteLine(arg2);
		IntArg = arg1 + arg2;
		StringArg = IntArg.ToString();
	}

	public Test(int[] args)
	{
		Console.Write("Constructor int ");
		Console.WriteLine(args.Length);
		IntArg = 0;
		foreach(var arg in args) {
			IntArg += arg;
		}
		StringArg = IntArg.ToString();
	}

	public static Test Parse(string arg)
	{
		Console.Write("Parse string ");
		Console.WriteLine(arg);
		return new Test(arg);
	}

	public static Test Parse(double arg)
	{
		Console.Write("Parse double ");
		Console.WriteLine(arg);
		return new Test(arg.ToString());
	}

	public static Test Parse(int arg)
	{
		Console.Write("Parse int ");
		Console.WriteLine(arg);
		return new Test(arg);
	}

	public static bool TryParse(string arg, out Test res)
	{
		Console.Write("TryParse ");
		Console.WriteLine(arg);
		var ret = false;
		try {
			res = new Test(arg);
			ret = true;
		} catch {
			res = null;
		}
		return ret;
	}
}

public class Test2
{
	public string StringArg2 { get; set; }
	public int IntArg2 { get; set; }

	public Test2()
	{
		Console.WriteLine("Constructor2 Default");
	}

	public Test2(Test arg)
	{
		Console.Write("Constructor2 Test ");
		Console.WriteLine(arg.IntArg);
		StringArg2 = arg.StringArg;
		IntArg2 = arg.IntArg;
	}

	public static Test2 Parse(Test arg)
	{
		Console.Write("Parse2 ");
		Console.WriteLine(arg.IntArg);
		return new Test2(arg);
	}

	public static bool TryParse(Test arg, out Test2 res)
	{
		Console.Write("TryParse2 ");
		Console.WriteLine(arg.IntArg);
		var ret = false;
		try {
			res = new Test2(arg);
			ret = true;
		} catch {
			res = null;
		}
		return ret;
	}

	public static explicit operator Test2(Test arg)
	{
		Console.Write("Cast2 ");
		Console.WriteLine(arg.IntArg);
		return new Test2(arg);
	}
}
"@
Add-Type $src

このスクリプトのメソッド名を変えたり、渡すものを変えて試験しました。