Методы с параметрами переменной длины
Методы
Классы
Упакованные типы по значению.
Типы по значению, как мы уже знаем, хранятся в стеке. Так для хранения типа int требуется всего лишь 4 байта, но вместе с тем, мы знаем, что все типы унаследованы от класса System.Object, а, следовательно, они унаследовали от него некоторые член - функции, например, такую, например, как функцию преобразования в строковое представление числовых данных - ToString().
Мы можем вызвать этот функцию, например, так:
int a = 25;
string str = a.ToString();
или даже:
string S = 75.ToString();
Здесь а и 75 выступают как полноценные объекты, имеющие присущие им член функции. Понятно, что для хранения таких объектов 4 байта в принципе не достаточно. При любом обращении к функциональности типа по значению - автоматически создается его временная копия в области памяти, называемой кучей. Копия типа по значению в куче - это ссылочный тип, обладающий полной функциональностью. Преобразование типа по значению, хранящегося в стеке, в ссылочный тип в куче называется упаковкой. После использования функциональных возможностей копия уничтожается.
Упаковку объекта можно выполнить явно, например:
int b = 45;
…
object c = b;
Здесь создается объект с ссылочного типа в куче, являющийся копией объекта по значению b.
Такое преобразование используется тогда, когда требуется передать переменную в качестве параметра функции, принимающий параметр по ссылке.
И наоборот, часто возникает задача распаковки упакованного объекта:
int z = (int) c; //явное преобразование типа.
Распаковываться может только упакованный объект.
Классы это абстрактный тип данных, определяемый пользователем (программистом).
Синтаксис объявления класса выглядит следующим образом:
class имя_типа
{
// члены класса
}
Класс является дальнейшим развитием структуры языка программирования «C».
Классы содержат член данные и член функции для работы с этими данными.
Доступ к членам класса управляем. Имеется четыре спецификации доступа:
public – общедоступный (доступ вне класса)
protected – защищенный член, к нему могут обращаться только члены данного класса и производных от него классов
private – член доступен только членам данного класса (производным классам он не доступен)
internal – член доступен только в пределах данной сборки и нигде больше.
Каждый член класса, если спецификация доступа отлична от private, должен быть явно объявлен с модификатором доступа. По умолчанию, то есть, если спецификация доступа опущена, устанавливается спецификация private.
К данным класса относятся:
– поля,
– константы,
– события.
Поле – это любые переменные объявленные в классе, или другими словами – это обычная член-переменная, содержащая некоторое значение.
Можно, например, объявить класс, членами которого являются только поля:
class CA
{
public int x;
protected float z;
double m;
public char sim;
private decimal sum;
}
Обратите внимание, на то, что поле m здесь объявлено по умолчанию приватным.
Константы – это поле, объявленное с модификатором const, или, другими словами это поле, значение которого изменить нельзя, например:
public const int x = 25;
События – являются членами класса, позволяющие объекту класса информировать клиента класса об изменениях (событиях), произошедших при выполнении кода, например, изменении какого-либо поля.
Член-данные класса, в отличие от членов-данных структуры, при объявлении класса разрешается инициализировать. Член-данные структуры при объявлении структуры инициализировать запрещено.
Поля класса можно, с помощью модификатора static, объявлять как статические.
Например:
using System;
namespace ConsoleApplication2
{
class CA
{
public static int x;
public int y = 25;
}
class Class1
{
[STAThread]
static void Main(string[] args)
{
CA pa = new CA();
int k = pa.y;
CA.x = 45;
int z = CA.x;
Console.WriteLine("k = {0} z = {1}", k, z);
}
}
}
При обращении к статическому полю используется имя класса, а не имя объекта этого класса. Попытка обратиться к статическому полю через имя объекта класса (например: pa.x) приведет к ошибке. Статическое поле класса хранится в единственном экземпляре для всех объектов класса (оно общее).
Все нестатические функции класса имеют неограниченный доступ ко всем член данным класса независимо от спецификации доступа.
Вопросы:
1. Какие преимущества и недостатки имеют типы по значению, определяемые пользователем, по сравнению с типами по ссылке?
2. Какие ограничения накладываются на структуру по сравнению с классом?
3. Разрешается ли программисту определять свой конструктор по умолчанию для структуры?
4. Как объявляется класс?
5. Разрешается ли в объявлении структуры и класса инициализировать их член – данные?
6. Какие спецификации используются в классе и структуре для управления доступом к членам класса?
7. Чем отличаются по своему действию спецификации private от protected?
8. Какая спецификация доступа к данным устанавливается по умолчанию, при объявлении класса?
9. Какая спецификация доступа к данным устанавливается по умолчанию, при объявлении структуры?
10. Чем отличаются статические поля класса от нестатических полей?
Член функции класса
Член функции класса в свою очередь подразделяются:
1. Методы класса
2. Свойства
3. Конструкторы
4. Деструкторы
5. Индексаторы
6. Операции
Методы являются функциями общего назначения. Остальные функции класса – это специализированные функции класса.
Методы – аналогичны функциям языка С и синтаксически объявляются так же:
Тип _значения имя_метода (список принимаемых параметров)
{
…//тело метода
}
Возврат из метода так же, как и в С осуществляется с помощью оператора return.
С помощью return можно возвращать как типы по значению, так и типы по ссылке, это означает, что можно возвращать массивы, так как массивы относятся к типам по ссылке.
Параметры методу передаются в C#, если не оговорено иное, только по значению. Это означает, что типы по значению будут переданы по значению, а для типов по ссылке будут передана по значению ссылка, указывающая на этот тип.
Часто необходимо, чтобы метод возвращал более одного значения. Это в языке С достигалось с помощью выходных значений. Для этого функции передавались указатели, с помощью которых осуществлялся доступ к памяти, где хранятся переменные, и значение их изменялось. В C# того же самого эффекта для переменной, имеющей тип по значению, можно достичь, если использовать ключевое слово ref.
void func(int a, ref int b)
{
// тело метода
}
Ключевое слово ref должно быть указано и при вызове функции:
func(k, ref m);
Здесь любое изменение значения параметра b в теле функции вызовет адекватное изменение переменной m в памяти.
С# не допускает использование переменной, если ее значение не определено. То есть значение переменной с модификатором ref должно быть определено до момента вызова функции. Если же значение переменной может быть определено только внутри тела функции, то необходимо использовать ключевое слово out, позволяющее передавать методу значение не инициализированной переменной.
void func(int a, ref int b, out int b)
{
// Тело метода
}
Переменная b передается по ссылке. Ключевое слово out необходимо использовать и при вызове функции:
func (c, ref d, out k);
Если параметру «k» в теле метода не будет присвоено никакого значения, то произойдет ошибка трансляции.
Ключевые слова ref и out сообщают транслятору, что адреса параметров и переменных, передаваемых в качестве параметров, совпадают. В этом случае изменение значения параметра внутри функции приведет к изменению значения переменной в вызывающем коде.
В C# методы могут принимать произвольное (переменное) количество параметров, в том числе и нулевое. Для этого используется модификатор params, с помощью которого объявляется параметр-массив. Количество элементов в массиве будет равно числу аргументов, переданных методу.
Рассмотрим пример определения минимального и максимального значения параметров передаваемых методу. Количество параметров, передаваемых методу, от вызова функции к вызову функции может быть различно:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication13
{
class Program
{
class valMnMx
{
public int max;
public int min;
}
class MnMx
{
public static valMnMx MinMax(params int[] nmbr)
{
if (nmbr.Length == 0)
{
Console.WriteLine("Ошибка! Список параметров пуст");
return null;
}
valMnMx mxmn = new valMnMx();
mxmn.max = mxmn.min = nmbr[0];
foreach (int a in nmbr)
{
if (a<mxmn.min )
mxmn.min=a;
if (a > mxmn.max)
mxmn.max = a;
}
return mxmn;
}
}
static void Main(string[] args)
{
int a = 16, b = -6, c = -78, d = 100, e = 0;
valMnMx res=new valMnMx() ;
res= MnMx.MinMax(a,b,e);
if(res!=null)
Console.WriteLine("мин={0}\nмакс={1}", res.min, res.max);
res = MnMx.MinMax(a, b, e, c, d);
if (res != null)
Console.WriteLine("мин={0}\nмакс={1}", res.min, res.max);
res = MnMx.MinMax();
if (res != null)
Console.WriteLine("мин={0}\nмакс={1}", res.min, res.max);
}
}
}
Результат работы программы:
мин=-6
макс=16
мин=-78
макс=100
Ошибка! Список параметров пуст
Для продолжения нажмите любую клавишу . . .
Наряду с обычными параметрами методы могут иметь и параметр переменной длины. Когда метод принимает обычные параметры и params-параметр, params-параметр должен стоять в списке параметров последним:
public static valMnMx MinMax(int a,doudle b,params int[] nmbr)
{
:
:
}
Методы, также как и поля, могут быть объявлены с модификатором static. В этом случае, для вызова метода достаточно указать имя класса, в котором он определен.
Например:
class CA
{
public static int min (int x, int y)
{
int z;
z = (x <= y) ? x : y;
return z;
}
}
class Class1
{
static void Main(string[] args)
{
int a = -5;
int b = 7;
Console.WriteLine(“a={0} b ={1}”,a,b);
int k = CA.min(a,b);
Console.WriteLine(“k=”+k);
}
}
Статически объявленные функции (методы) считаются связанными с классом, а не с объектом этого класса.
1. Статически объявленные методы не должны пытаться получить доступ к не статическим членам класса.
2. Статически объявленные методы не могут быть вызваны как методы экземпляра класса.
Например, если создать экземпляр класса следующим образом:
CA p = new CA();
то обращение к полю:
p.m = 25;
будет правильно, а обращение к статическому методу следующим образом:
int k = p.min(a,b);
приведет к ошибке компиляции.
В С# различают статические поля и методы, принадлежащие классу, и поля экземпляра и методы экземпляра, принадлежащие объекту.
Методы экземпляра могут обратиться к статическим полям и вызвать статические методы класса.