Именованные аргументы и вывод универсального типа в C # 4.0 - PullRequest
56 голосов
/ 01 июля 2011

Я программировал в предположении, что при вызове метода в C # 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");

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

Аргументы типа для метода '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);
}

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

Ответы [ 3 ]

16 голосов
/ 04 июля 2011

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

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

Хорошо, я полагаю, как и другие определили, это ошибка, о которой следует сообщить в Microsoft !!

6 голосов
/ 04 июля 2011

Просто хочу сообщить, что является ошибкой, специфичной для C # (и @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
0 голосов
/ 07 июля 2011

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

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