2016-09-03 3 views
1

мне очень интересно, почему ни один из следующих методов DoInvoke можно назвать только один Params:C# Выбор PARAMS аргумент при использовании дважды

public class foo { 
    private void bar(params object[] args) { 
     DoInvoke(args); 
    } 

    //Error: There is no argument given that corresponds to the required formal parameter 'args' of 'foo.DoInvoke(Delegate, object[])' 
    private void DoInvoke(Delegate d, object[] args) { 
     d.DynamicInvoke(args); 
    } 

    //Error: Argument 1: cannot convert from 'object[]' to 'System.Delegate' 
    private void DoInvoke(Delegate d, params object[] args) { 
     d.DynamicInvoke(args); 
    } 
} 

Я уже нашел способ, который не злоупотреблять Params. Мне любопытно, почему здесь не расширены параметры.

Я был в состоянии сделать что-то подобное в Lua, отсюда и моя попытка. Я знаю, что Lua гораздо менее строг, но я не уверен, какое правило C# я нарушаю, делая это.

+5

Вы вызываете DoInvoke без передачи делегата, который не является необязательным параметром. –

+0

, даже если это был необязательный параметр типа '(Delegate d = null,', он все равно не будет работать. Не знаете, как это работает в Lua, но откуда вы ожидаете, что 'd' произойдет, когда вы вызовете функцию не передавая ему делегат? – Slai

+0

@ Ювал Ицхаков Аргс определяется как params в bar(), поэтому почему это не распространяется на (Delegate, object []), когда делегат передается как первый аргумент bar(). – Zatronium

ответ

2

Мне интересно, почему ни один из следующих методов DoInvoke можно назвать только один Params:

Короткая версия: первый не может, потому что у него есть два не необязательные параметры и, потому что вы передаете значение неправильного типа для первого необязательного параметра. Второе не может, но только потому, что значение, которое вы пытаетесь передать для одного необязательного параметра, имеет неправильный тип; второй параметр является необязательным и поэтому может быть опущен, как вы это делали.


Вы, кажется, под впечатлением, что в вашем методе декларации private void bar(params object[] args), наличие params ключевого слова делает args переменную как-то отличается от любой другой переменной. Это не. Ключевое слово params затрагивает только сайт вызова, позволяющий (но не , требующий) вызывающего, чтобы указать элементы массива переменной args, которые должны быть указаны, как если бы они были отдельными параметрами, а не явным образом создавали массив.

Но даже тогда, когда вы вызываете bar(), происходит то, что объект массива создается и передается в bar(), поскольку любой другой массив будет передан. Переменная args внутри метода bar() представляет собой всего лишь массив. Он не получает никакой специальной обработки, и компилятор не будет (например) неявно расширять его до списка параметров для использования при передаче другому методу.

Я не знаком с Lua, но это несколько контрастирует с вариационными функциями в C/C++, где язык предоставляет способ распространения списка параметров переменных для дальнейшего отклонения. В C# единственным способом прямого распространения списка параметров params является то, что вызывающий может принять точный тип массива, объявленный в вызывающем (который из-за дисперсии типа массива в C# не всегда должен быть точно таким же тип, но по-прежнему ограничен).


Если вам интересно, соответствующие C# спецификации языка адреса это в разных местах, но в первую очередь в «7.5.1.1 Соответствующие параметры». Это читает (от C# 5 спецификации & hellip; есть проект C# 6 спецификации, но C# 5 в основном то же самое, и это то, что у меня есть копия):

Для каждого аргумента в списке аргументов там есть быть соответствующим параметром в члене функции или делегированном вызове.

Он продолжает описывать, что «список параметров» используется для проверки списка аргументов, но в вашем простом примере, разрешение перегрузки уже произошло в момент это правило применяется, и таким образом, есть только один параметр список, о котором нужно беспокоиться:

• Для всех других членов функции и делегатов имеется только один список параметров, который используется.

Он продолжает говорить:

Соответствующие параметры для аргументов членов функции устанавливаются следующим образом:
• Аргументы в аргументе-список конструкторов экземпляров, методов, индексаторов и делегатов:
        o Позиционный аргумент, в котором фиксированный параметр встречается в одном и том же положении в списке параметров, соответствует этому параметру.[курсив мой]
        о позиционном аргументе функции члена с массивом параметров вызываются в его нормальной форме соответствует массиве параметров, которые должны происходить в той же позиции в списке параметров.
      o Позиционный аргумент элемента функции с массивом параметров, вызываемым в его расширенной форме, где фиксированный параметр не встречается в одной и той же позиции в списке параметров, соответствует элементу массива параметров.
        o Именованный аргумент соответствует параметру с тем же именем в списке параметров.
        o Для индексировщиков при вызове средства доступа к набору выражение, указанное как правый операнд оператора присваивания, соответствует параметру неявного значения объявления-адаптера набора.

Другими словами, если вы не укажете имя параметра в списке аргументов, аргументы соответствуют параметрам метода по позиции. А параметр в первой позиции обоих вызываемых методов имеет тип Delegate.

При попытке вызвать первый метод этот метод имеет нулевые необязательные параметры, но вы не указали второй параметр. Таким образом, вы получаете сообщение об ошибке, указывающее, что список аргументов, состоящий только из одного аргумента (который по приведенному выше соответствует параметру Delegate d), не содержит второго аргумента, который бы соответствовал параметру object[] args в вызываемом методе.

Даже если вы предоставили второй аргумент, вы столкнулись бы с той же ошибкой, с которой вы пытаетесь вызвать второй пример метода. То есть в то время как параметр params object[] args является необязательным (компилятор предоставит пустой массив для вызова), и поэтому вы можете уйти с предоставлением только одного аргумента в своем вызове методу, что один аргумент имеет неправильный тип. Его позиционное соответствие относится к параметру Delegate d, но вы пытаетесь передать значение типа object[]. Нет перевода с object[] на Delegate, поэтому вызов завершается с ошибкой.


Итак, что это значит для реального кода? Ну, это зависит от того, что вы пытаетесь сделать. Что вы ожидали, когда вы пытались передать свою args переменную в метод void DoInvoke(Delegate d, params object[] args)?

Очевидной возможностью является то, что массив args содержит в качестве своего первого элемента объект Delegate, а оставшаяся часть массива - это аргументы для передачи. В этом случае, вы могли бы сделать что-то вроде этого:

private void bar(params object[] args) { 
    DoInvoke((Delegate)args[0], args.Skip(1).ToArray()); 
} 

Это должно быть синтаксически правильным ни с одним из методов DoInvoke() вы показали. Конечно, неясно, действительно ли это то, что вам нужно, так как я не знаю, что должен был сделать звонок.

+0

Я изначально считал, что вы делаете то, что вы предлагаете, с помощью args в вашем повторном примере, но в конце концов я решил, что передача делегата и аргументов таким образом просто вызвала проблемы. Вместо этого я обхожу класс, содержащий делегат и аргументы, так что изменение количества аргументов bar() не приведет к появлению кодового кода позже. Теперь есть меньше приведения типов и нет двусмысленного использования параметров. Я спросил, потому что мне было любопытно, как C# отличался от реализации параметров, и этот ответ имеет смысл. – Zatronium

Смежные вопросы