为什么动态调用调度更喜欢双重转换而不是调用对象重载?

时间:2018-12-04 08:16:12

标签: c# dynamic implicit-conversion dynamic-language-runtime

我正在使用动态调度(以简化处理泛型)将任意对象树转换为某些特定格式。对象树由互操作代码生成,将一些值包装到容器中,这些容器上定义了隐式转换。

处理这些容器时,我打了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

0 个答案:

没有答案