Смогут ли абоненты нижнего уровня использовать дополнительные параметры в сборках, созданных с помощью C # 4.0? - PullRequest
2 голосов
/ 11 июня 2009

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

Type2 _defaultValueForParam2 = foo;
Type3 _defaultValueForParam3 = bar;

public ReturnType TheMethod(Type1 param1)
{
   return TheMethod(param1, _defaultValueForParam2);
}
public ReturnType TheMethod(Type1 param1, Type2 param2)
{
   return TheMethod(param1, param2, _defaultValueForParam3);
}
public ReturnType TheMethod(Type1 param1, Type2 param2, Type3 param3)
{
   // actually implement the method here. 
}

И я понимаю, что необязательные параметры в C # должны позволить мне обобщить это до одного метода. Если я создаю метод с некоторыми параметрами, помеченными как необязательные, будет ли он работать с вызывающими сборками нижнего уровня сборки?


РЕДАКТИРОВАТЬ : Под "работой" я подразумеваю, что вызывающая сторона нижнего уровня , приложение, скомпилированное с помощью компилятора C # 2.0 или 3.5, сможет вызывать метод с одним, двумя или три параметра, как если бы я использовал перегрузки, и компилятор нижнего уровня не будет жаловаться.

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

Ответы [ 3 ]

1 голос
/ 12 июня 2009

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

Когда вы вызываете метод, аргументы помещаются в стек. Если переданы три 32-битных аргумента, то в стек будут помещены 12 байтов; если переданы четыре 32-битных аргумента, то в стек будут помещены 16 байтов. Количество байтов, помещаемых в стек, неявно указано в вызове: вызываемый объект предполагает, что передано правильное количество аргументов.

Таким образом, если функция принимает четыре 32-битных параметра, она будет искать в стеке 16 байтов, предшествующих адресу возврата вызывающей стороны. Если вызывающий передал только 12 байтов, то вызываемый будет считывать 4 байта из того, что уже было в стеке, прежде чем был сделан вызов. Он не может знать, что все 16 ожидаемых байтов не были переданы.

Вот как это работает сейчас. Это не изменится для существующих компиляторов.

Для поддержки необязательных параметров должно произойти одно из двух:

  1. Вызывающий может передать дополнительное значение, которое явно сообщает вызываемому, сколько аргументов (или байтов) было помещено в стек. Затем вызываемый абонент может заполнить значения по умолчанию для любых пропущенных параметров.
  2. Вызывающий может продолжить передачу всех объявленных параметров, подставив значения по умолчанию (которые будут считаны из метаданных вызываемого) для любых необязательных параметров, пропущенных в коде. Затем вызываемый объект считывает все значения параметров из стека, как это происходит сейчас.

Я подозреваю, что это будет реализовано, как в (2) выше. Это похоже на то, как это делается в C ++ (хотя C ++, в котором отсутствуют метаданные, требует, чтобы параметры по умолчанию были указаны в заголовочном файле), более эффективно, чем опция (1), поскольку все это делается во время компиляции и не требует дополнительного значения, помещаемого в стек, и является наиболее простой реализацией. Недостаток опции (2) состоит в том, что, если значения по умолчанию изменяются, все вызывающие программы должны быть перекомпилированы, иначе они будут продолжать передавать старые значения по умолчанию, так как они были скомпилированы как константы. Это похоже на то, как теперь работают публичные константы. Обратите внимание, что опция (1) не имеет этого недостатка.

Опция (1) также не поддерживает передачу именованных параметров, поэтому функция, объявленная следующим образом:

static void Foo(int a, int b = 0, int c = 0){}

это можно назвать так:

Foo(1, c: 2);

Параметр (1) можно изменить, чтобы учесть это, сделав дополнительное скрытое значение битовой картой пропущенных аргументов, где каждый бит представляет один необязательный параметр. Это произвольно ограничивает число необязательных параметров, которые может принять функция, хотя, учитывая, что это ограничение будет по крайней мере 32, это может быть не так уж плохо. Однако крайне маловероятно, что это фактическая реализация.

При любой реализации вызывающий код должен понимать механизм необязательных параметров, чтобы пропустить любые аргументы в вызове. Кроме того, с опцией (1) должен быть передан дополнительный скрытый параметр, о котором старые компиляторы даже не знали бы, если бы он не был добавлен в качестве формального параметра в метаданные.

1 голос
/ 11 июня 2009

В c # 4.0, если необязательный параметр опущен, подставляется значение по умолчанию для этого параметра: wit:

public void SendMail(string toAddress, string bodyText, bool ccAdministrator = true, bool isBodyHtml = false)
{ 
    // Full implementation here   
}

Для вызывающих абонентов нижнего уровня это означает, что если они используют один из вариантов, в котором отсутствуют параметры, c # будет заменять значение по умолчанию, которое вы указали для отсутствующего параметра . Эта статья объясняет процесс более подробно .

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

0 голосов
/ 11 июня 2009

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

...