Именованные аргументы и общий тип вывода в С# 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);
}
Кстати, я наблюдал такое же поведение с нестатическими методами. Я просто решил использовать статические методы, чтобы сделать здесь пример немного короче.
Ответы
Ответ 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, поэтому он просит программиста явно указать это.