我正在使用动态调度(以简化处理泛型)将任意对象树转换为某些特定格式。对象树由互操作代码生成,将一些值包装到容器中,这些容器上定义了隐式转换。
处理这些容器时,我打了StackOverflowException
。最初,我认为可能是一个愚蠢的错误,我只是以相同的值调用Accept
,但是,我设法将问题缩小到这个范围:
using System;
using System.Runtime.CompilerServices;
class Program {
static void Main(string[] args) {
var container = new Container<object>("42");
Accept((dynamic)container);
}
static void Accept(object obj) { Console.WriteLine(nameof(obj)); }
static void Accept(int i32) { Console.WriteLine(nameof(i32)); }
static void Accept<T>(IContainer<T> container) {
RuntimeHelpers.EnsureSufficientExecutionStack();
Accept((dynamic)container.Get());
}
}
public interface IContainer<out T>{
T Get();
}
public struct Container<T>: IContainer<object> {
readonly T value;
public Container(T value){ this.value = value; }
public static implicit operator Container<T>(T value) => new Container<T>(value);
public object Get() => this.value;
}
令人惊讶的是,EnsureSufficientExecutionStack
失败了。显然,无论是对container.Get()
的值执行动态分配,还是将结果包装回Container<object>
并将结果传递给Accept<T>(IContainer<T>)
,而不是直接传递给Accept(object)
。>
问题是:选择这样做时遵循什么逻辑?当我有Accept<T>(Container<T>)
时,这有点道理,因为我猜到它可以调用隐式转换,但是我无法理解它怎么找到对某些任意接口实现的隐式转换,以分派给重载。接口参数。
让我完全震惊的是,如果在
Container<object>? container = new Container<object>("42");
我将object
替换为string
,错误消失了,程序正确打印了obj