Почему динамическая диспетчеризация вызовов предпочитает двойное преобразование вызову перегрузки объекта? - PullRequest
0 голосов
/ 04 декабря 2018

Я работаю над произвольным преобразованием дерева объектов в какой-то определенный формат, используя динамическую диспетчеризацию (чтобы упростить обработку обобщений).Дерево объектов генерируется кодом взаимодействия, упаковывает некоторые значения в контейнеры, для которых определены неявные преобразования.

Когда я обрабатываю эти контейнеры, я нажимаю 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

...