Подтвердить что ты не робот

Именованные аргументы и общий тип вывода в С# 4.0

Я программировал в предположении, что при вызове метода в С# 4.0 предоставление имен для ваших аргументов не повлияет на результат, если только вы не пропустили один или несколько необязательных параметров.

Итак, я был немного удивлен, обнаружив следующее поведение:

Учитывая метод, который принимает Func<T>, выполняет его и возвращает результат:

public static T F<T>(Func<T> f)
{
    return f();
}

И еще один метод, из которого виден описанный выше метод:

static void Main()
{
    string s;

вызов F (без именованных аргументов) компилируется без каких-либо проблем:

    s = F<string>(() => "hello world"); // with explicit type argument <string>
    s = F(() => "hello world"); // with type inference

И при использовании именованного аргумента...

    s = F<string>(f: () => "hello world");

... приведенная выше строка кода с использованием аргумента явного типа все еще компилируется без проблем. И, может быть, не слишком удивительно, если у вас установлен ReSharper, это будет предполагать, что спецификация аргументов типа "избыточна".

Однако при удалении аргумента типа...

    s = F(f: () => "hello world");

компилятор С# сообщит об этой ошибке:

Аргументы типа для метода "Program.F(System.Func)" не могут быть выведены из использования. Попробуйте явно указать аргументы типа.

Есть ли логическое объяснение этого взаимодействия между именованными аргументами и типом вывода?

Является ли это поведение документированным где-то в спецификации языка?

Я понимаю, что мне вообще не нужно называть аргумент. Тем не менее, я обнаружил это поведение в гораздо более сложном сценарии, где я думал, что имеет смысл назвать аргументы в моем вызове метода для внутренних целей документации. Я не спрашиваю, как обойти эту проблему. Я пытаюсь понять некоторые тонкости языка.

Чтобы сделать что-то интереснее, я обнаружил, что следующие компиляции без проблем:

    Func<string> func = () => "hello world";
    s = F<string>(func);
    s = F(func);
    s = F<string>(f: func);
    s = F(f: func);
}

Кстати, я наблюдал такое же поведение с нестатическими методами. Я просто решил использовать статические методы, чтобы сделать здесь пример немного короче.

4b9b3361

Ответ 1

Вывод не является чем-то, что будет работать на многих вложенных уровнях в компиляции. Это своего рода предположение, основанное на приведенных аргументах. Я считаю, что авторы компилятора не рассматривали логику логики вместе с именованным параметром. Если вы рассматриваете абстрактное синтаксическое дерево, хотя логика такая же, но оба   F (() = > "XYZ" ) А также   F (F:() = > "XYZ" ) Существуют различные абстрактные деревья синтаксиса из перспективы компилятора.

Я чувствую, что это просто правило, пропущенное разработчиком компилятора, где даже сам компилятор представляет собой программу с огромным набором правил. Одно правило соответствует первому случаю, но ни одно правило не соответствует второму. Это может быть концептуально правильно, но компилятор - это просто программа, и все правила кодируются человеком.

Хорошо, я думаю, как выяснили другие, его ошибка и должна быть сообщена Microsoft!

Ответ 2

Просто хочу сообщить, что это ошибка, характерная для С# (и @leppie. Я подтвердил, что она не работает со стандартным csc.exe, даже в Visual Studio). Избыточное указание именованного аргумента вообще не должно вызывать изменения в поведении.

Об ошибке сообщается в Microsoft Connect.

Эквивалентный VB отлично работает (поскольку он компилируется, я добавил немного, чтобы подтвердить, что поведение во время работы соответствует ожиданиям):

Imports System

Module Test
  Function F(Of T)(ByVal fn As Func(Of T)) As T
    Return fn()
  End Function

  Function Inc(ByRef i as Integer) As String
    i += 1
    Return i.ToString
  End Function

  Sub Main()
    Dim s As String
    s = F(Of String)(Function()"Hello World.")
console.writeline(s)
    s = F(Function()"Hello, World!")
console.writeline(s)
    s = F(Of String)(fn:=Function()"hello world.")
console.writeline(s)
    s = F(fn:=Function()"hello world")
console.writeline(s)
    Dim i As Integer
    Dim func As Func(Of String) = Function()"hello world " & Inc(i)
    s = F(Of string)(func)
console.writeline(s)
    s = F(func)
console.writeline(s)
    s = F(Of string)(fn:=func)
console.writeline(s)
    s = F(fn:=func)
console.writeline(s)
  End Sub
End Module

Вывод:

Hello World.
Hello, World!
hello world.
hello world
hello world 1
hello world 2
hello world 3
hello world 4

Ответ 3

Вызов функции с именованными параметрами и без именованных параметров не совпадает. В случае именованных параметров компилятор принимает другой путь, так как сначала необходимо разрешить именованные параметры. В вашем случае компилятор пытается определить параметр f до разрешения T в F, поэтому он просит программиста явно указать это.