Типы параметров методов

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

Здесь нет константных параметров, как в С++, и параметрам методов можно присваивать значения. Если только параметры не объявлены определенным образом — как ref или out — такое присваивание остается локальным в пределах метода.

Обнаружилось, что одной из наибольших заминок для разработчиков С++ в языке С# стало обращение с семантикой переменных, передаваемых в методы. Поскольку доминантным типом внутри CLR является ссылка, переменные таких объектов просто указывают на экземпляры в куче, т.е. аргументы передаются в метод в соответствие с семантикой ссылки.

Разработчики С++ привыкли к тому, что по умолчанию в методы передаются копии переменных, если только они не переданы по ссылке или в виде указателя. Другими словами, в С++ аргументы передаются согласно семантике значений.

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

Поскольку в С# нет нотации константных параметров, для передачи константных параметров должны создаваться неизменяемые (immutable) объекты.

Разработчики на С++, которые привыкли применять идиомы дескриптор/тело (handle/ body) для реализации семантики копирования при записи, должны принимать этот факт во внимание. Это не значит, что применять эти идиомы в С# невозможно; скорее, это просто означает, что они должны быть реализованы иначе.

Аргументы-значения

В действительности все параметры, передаваемые в методы, являются аргументами-значениями, если предполагается, что они — нормальные, простые, недекорирован-ные параметры методов. Под недекорированными понимается отсутствие специальных ключевых слов, таких как out, ref и params, которые могут быть присоединены к ним.

Однако они могут иметь присоединенные атрибуты, как почти все что угодно в системе типов CLR.

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

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

То есть любые изменения, проведенные в объекте через такую ссылку, становятся видимыми коду, вызвавшему метод.

Параметры out

Параметры out (выходные) почти идентичны параметрам ref, но с двумя существенными отличиями. Во-первых, вместо ключевого слова ref используется ключевое слово out, и оно также должно применяться в точке вызова, как это было в случае ref.

Во-вторых, переменная, на которую ссылается переменная out, не обязана иметь присвоенное значение перед вызовом метода, как в случае ref-параметра. Причина в том, что методу не позволено каким-либо полезным способом использовать переменную до тех пор, пока ей не будет присвоено значение.

Например, следующий код совершенно корректен:

1
2
3
4
5
6
7
8
9
10
11
public class EntryPoint
{
static void Main()  {
object obj;
PassAsOutParam ( out obj );
}
static void PassAsOutParam ( out object obj )
{
obj = new Object () ;
}
}

Обратите внимание, что переменной obj в методе Main перед вызовом PassAsOutParam значение напрямую не присваивается. И это правильно, потому что она помечена как out-параметр. Метод PassAsOutParam не обращается к этой переменной, пока не присвоит ей значение.

Если вы попробуете заменить два вхождения out на ref в приведенном выше коде, то получите ошибку компиляции, подобную следующей:

error CS0165: Use of unassigned local variable ‘obj’
ошибка CS0165: Использование неинициализированной локальной переменной obj

Массивы params

С# позволяет легко передавать переменный список параметров. Для этого просто объявите последний параметр в списке параметров с типом массива, предварив его ключевым словом params.

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

Ниже приведен краткий пример:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
public class EntryPoint
{
static void Main()  {
VarArgs ( 42 ) ;
VarArgs ( 42, 43, 44 ) ;
VarArgs ( 44, 56, 23, 234, 45, 123 );
}
static void VarArgs ( int vail, params int[] vals )  {
Console.WriteLine ( "vail: {0}", vail );
foreach ( int i in vals )  {
Console.WriteLine ( "vals[]:  {0}", i ) ;
}
Console.WriteLine ();
}

В каждом случае VarArgs вызывается успешно, но в каждом случае массив, переданный в vals, отличается. Как видите, передача переменного числа параметров в С# замечательно проста. Можете построить эффективный метод Add для контейнерного типа, применяя массивы параметров, когда для добавления переменного количества элементов необходим только один вызов.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100