Разрешение перегрузки с неявными преобразованиями - PullRequest
5 голосов
/ 20 марта 2020

Мне в основном нужны две отдельные перегрузки для string / FormattableString (фоном является то, что я хочу подтолкнуть людей к использованию строковых констант для сообщений журнала и передачи параметров через структурированное ведение журнала вместо сообщения журнала для упрощения анализа. Поэтому метод ведения журнала FormattableString будет устаревшим).

Теперь, из-за того, как работает компилятор, вы не можете напрямую перегрузить методы, потому что FormattableString передается в строку до ее передачи. Что работает, так это наличие структуры-оболочки, которая определяет неявные перегрузки:

public struct StringIfNotFormattableStringAdapter
{
    public string StringValue { get; }

    private StringIfNotFormattableStringAdapter(string s)
    {
        StringValue = s;
    }

    public static implicit operator StringIfNotFormattableStringAdapter(string s)
    {
        return new StringIfNotFormattableStringAdapter(s);
    }

    public static implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)
    {
        throw new InvalidOperationException("This only exists to allow correct overload resolution. " +
                                            "This should never be called since the FormattableString overload should be preferred to this.");
    }
}

public static class Test
{
    public static void Log(StringIfNotFormattableStringAdapter msg)
    {
    }

    public static void Log(FormattableString msg)
    {
    }

    public static void Foo() 
    {
         Log("Hello"); // resolves to StringIfNotFormattableStringAdapter overload
         Log($"Hello"); // resolves to FormattableString overload 
    } 

}

Пока все хорошо.

Что я не понимаю: почему удаление

implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)

приводит к тому, что вызов Log($"Hello") становится двусмысленным?

CS0121 Вызов неоднозначен между следующими методами или свойствами: Test.Log (StringIfNotFormattableStringAdapter) 'и' Test.Log (FormattableString) '`

1 Ответ

4 голосов
/ 23 марта 2020

Согласно спецификации C#, Интерполированные строки , существует неявное преобразование из интерполированной строки в FormattableString:

interpolated_string_expression - это классифицируется как стоимость. Если он немедленно преобразуется в System.IFormattable или System.FormattableString с неявным преобразованием интерполированной строки ( Неявное интерполированное преобразование строки ), интерполированное строковое выражение имеет этот тип. В противном случае он имеет тип string.

. В предоставленном коде также выполняется преобразование string в StringIfNotFormattableStringAdapter.

Вызов метода

Log($"Hello");

можно разрешить для обоих методов Log, поскольку интерполированное строковое выражение $"Hello" может быть:

  • неявно преобразовано в FormattableString как интерполированная строка;
  • неявно преобразовано на StringIfNotFormattableStringAdapter как string.

Здесь возникает неоднозначность для компилятора, и для ее устранения необходимы дополнительные правила. Для устранения неоднозначности компилятор использует правила, описанные в C# Спецификация, Лучшая цель конверсии (go в нижней части страницы 164) . Правила гласят:

Учитывая два различных типа T1 и T2, T1 является лучшей целью преобразования, чем T2, если неявное преобразование из T2 в T1 существует, и по крайней мере одно из следующих условий:

  • Неявное преобразование из T1 в T2 существует

  • (другие правила не важны для нашего случая)

В приведенном коде FormattableString лучше преобразование, чем StringIfNotFormattableStringAdapter, потому что

  • нет неявного преобразование из StringIfNotFormattableStringAdapter в FormattableString

и

  • существует неявное преобразование из FormattableString в StringIfNotFormattableStringAdapter.

Поэтому компилятор предпочитает преобразовывать интерполированную строку $"Hello" в FormattableString, а затем вызывает метод Log(FormattableString).

Почему удаление

implicit operator StringIfNotFormattableStringAdapter(FormattableString fs)

вызывает вызов Log($"Hello") стать неоднозначным?

Потому что при удалении этого оператора второе правило («неявное преобразование из FormattableString в StringIfNotFormattableStringAdapter существует»). s ") прерывается, и теперь компилятор не может определить лучшую цель преобразования. Это приводит к неоднозначности для компилятора, и возникает ошибка компиляции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...