長い;読めぬ
こんなさむしんぐがあったとして、
class MyDynamic:DynamicObject
{
public override bool TryConvert(ConvertBinder binder, out object result)
{
Console.WriteLine($"\tTryCovert is called!");
result= Enumerable.Range(0, 10).ToArray();
return true;
}
}
static void Main(string[] args)
{
dynamic d = new MyDynamic();
int[] array = d;//1
array = (int[]) d;//2
IEnumerable<int> intIterator = d;//3
intIterator = (IEnumerable<int>) d;//4
}
こんな風にすると、なぜか④だけInvalidCastException
が飛んで来るから注意しましょうって話。
何が起きたのか
動かすとわかるけど、①~③は想定通りTryConvert
メソッドが呼ばれて動的に変換が解決されているコトがわかる。
けど、④だけはTryConvert
が呼ばれずInvalidCastException
が飛んでくる。
どーしてこうなった
これらの挙動はExplicit reference conversionsの、bullet 3みれば書いてあって
From any class_type S to any interface_type T, provided S is not sealed and provided S does not implement T.
と定義されてるから、まず明示的な型変換は可能であるのだけど、逆にPermitted user-defined conversionsのbullet 3に、
Neither S0 nor T0 is an interface_type.
とあって、任意の型にpublic static implicit operator <interface_type> (...)
やpublic static explicit operator <interface_type>(...)
みたいなことは出来ないよと言うことになる1
で、ここら辺の理由から、明示的な型変換を行った場合、interface-typeへのユーザー定義変換が動的型に対するコールサイト構築を行う際、全く考慮されてないんじゃないかなって。
で、結局考慮されてないわけだから、Interface-typeへの明示的な型変換は、一般的なC#の文脈で行われるってコトだと思う。
他方、暗黙の型変換が許容されるのは、右辺にある'd'が明示的な変換操作無しで変換可能であるかのように振る舞う必要があるため、コールサイト構築時にDynamicObject.TryConvert
が呼ばれる様なコールサイト構築を行うのではと。
じゃーどーすりゃいいのか
答えはこれ以上無いほどシンプルで、変換したいinterfaceを付けてやれば良い。
先の例だとこんな感じ
class MyEnumerableDynamic :DynamicObject,IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
Console.WriteLine("GetEnumerator was called.");
return Enumerable.Range(0, 10).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public override bool TryConvert(ConvertBinder binder, out object result)
{
Console.WriteLine($"\tTryCovert is called!");
if (binder.Type == typeof(int))
{
result = 0;
return true;
}
result = Enumerable.Range(0, 10).ToArray();
return true;
}
}
こーすれば、一般的な型変換のコンテキストで動いて問題なく変換できる。
参考にしたサイト
DynamicObject.TryConvert not called when casting to interface type
On Dynamic Objects and DynamicObject
-
実際コンパイルエラーになる。蛇足ながらこれで来ちゃうと色々とマズいとは思うので妥当じゃ有ると思う。 ↩