Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5677 / 802 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 3:

Управляющие операторы и методы

< Лекция 2 || Лекция 3: 123456 || Лекция 4 >

Работа с входным потоком. Предварительная информация

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

Следующий пример демонстрирует способы преобразования поступающей через входной поток (ввод с клавиатуры) информации с целью получения значений арифметического типа:

using System;

namespace InOutTest00
{
public class InputTester
{

// Ввод арифметических значений типов int и float.
// Осуществляется в рамках бесконечного цикла, поскольку
// предполагает возможные ошибки при вводе.
// Выход из бесконечного цикла
// происходит только после прочтения из входного потока
// последовательности символов, которая может быть
// преобразована в значения типа int или float.
public int intIput()
{
string inputStr;
int val = 0;
for ( ; ; )
{
Console.Write("integer value, please >>> ");
// Строка символов читается с клавиатуры.
inputStr = Console.ReadLine(); 
try
{
// Попытка преобразования строки символов к типу int.
// В случае неудачи возникает исключение. 
val = int.Parse(inputStr);
}
catch (Exception e)
{
// Перехват исключения.
// Сообщили об ошибке ввода.
Console.WriteLine("Input Error. {0} isn't integer", inputStr);
// Собственно информация об исключении.
Console.WriteLine("{0}", e.ToString());
// Повторяем попытку ввода символьной строки.
continue;
}
// Успешная попытка преобразования строки символов к типу int.
// Выход из цикла.
return val;
}
    
}

// Ввод значений типа float.
// Выход из бесконечного цикла
// происходит только после прочтения из входного потока
// последовательности символов, которая может быть
// преобразована в значение типа float.
// На правильность ввода влияют настройки языковых параметров,
// представления чисел, денежных единиц, времени и дат.


public float floatIput()
{
string inputStr;
float val;
for ( ; ; )
{
Console.Write("float value, please >>> ");
inputStr = Console.ReadLine();
try
{
// Приведение строки символов к значению типа float
// с использованием статических методов класса Convert. 
val = System.Convert.ToSingle(inputStr);
}
catch (Exception e)
{
Console.WriteLine("Input Error!");
Console.WriteLine("{0}", e.ToString());
continue;
}
return val;
}
}
}

class Program
{
// Точка входа. 
static void Main(string[] args)
{
// Создается объект – представитель класса InputTester. 
InputTester it = new InputTester();
// Этот объект используется для корректного ВВОДА с клавиатуры:
// значений типа int...  
int iVal = it.intIput();
// значений типа float...
float fVal = it.floatIput();
}
}
}
Листинг 3.3.

Перегрузка методов

Имя метода и список типов параметров метода являются его важной характеристикой и называются СИГНАТУРОЙ метода. В C# методы, объявляемые в классе, идентифицируются по сигнатуре. Эта особенность языка позволяет объявлять в классе множество одноименных методов. Такие методы называются перегруженными, а деятельность по их объявлению – перегрузкой.

При написании программного кода, содержащего ВЫРАЖЕНИЯ ВЫЗОВА переопределенных методов, корректное соотнесение выражения вызова метода определяет метод, которому будет передано управление:

// Класс содержит объявление четырех одноименных методов
// с различными списками параметров. 
class C1
{
void Fx(float key1)
{
return;
}

int Fx(int key1)
{
return key1;
}

int Fx(int key1, int key2)
{
return key1;
}

int Fx(byte key1, int key2)
{
return (int)key1;
}

static void Main(string[] args)
{
C1 c = new C1();
// Нестатические методы вызываются от имени объекта c. 
// Передача управления соответствующему методу
// обеспечивается явными преобразованиями к типу.
c.Fx(Convert.ToSingle(1));
c.Fx(3.14F);   
c.Fx(1);
c.Fx(1,2);
c.Fx((byte)10, 125);
}
}

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

Способы передачи параметров при вызове метода

Известно два способа передачи параметров при вызове метода:

  • по значению (в силу специфики механизма передачи параметров – только входные),
  • по ссылке (входные и/или выходные).

Передача по значению – БЕЗ спецификаторов (для типов-значений этот способ предполагается по умолчанию). Параметр представляет собой локальную копию значения в методе. В теле метода это означенная переменная, которую можно использовать в методе наряду с переменными, непосредственно объявленными в этом методе. При этом изменение значения параметра НЕ влияет на значение параметра в выражении вызова.

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

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

Выражение вызова метода приобретает свое значение непосредственно по возвращении из вызываемого метода. Этим значением можно воспользоваться только в точке возврата.

Переданные методу "для означивания" параметры со спецификатором out сохраняют свои значения за точкой возврата.

Спецификация способа передачи параметра является основанием для перегрузки метода.

using System;

class XXX {
public int mmm;
 }	

class Class1
 {//=============================================
static int i;
static void f1 (ref int x)
 {
x = 125; 
 }

static void f1 (int x)
 {
x = 0; 
 }

static void f1 (XXX par)
 {
par.mmm = 125;
 }

static void f1 (out XXX par)
 {
  par = new XXX(); // Ссылка на out XXX par ДОЛЖНА быть
  // обязательно проинициализирована в теле
  // метода НЕПОСРЕДСТВЕННО перед обращением к ней!
  // Способ инициализации – любой! В том числе и созданием объекта!
  // А можно и присвоением в качестве значения какой-либо другой ссылки. 
 par.mmm = 125;
 }

// Для параметра типа значения спецификаторы out–ref 
// неразличимы. 
//static void f1 (out int x)
//{
// x = 125; 
//}

static void Main(string[] args)
 {//======================================
int a = 0;
f1(ref a);
//f1(out a);
f1(a);

XXX xxx = new XXX();
xxx.mmm = 0;
f1(xxx);
f1(ref xxx);
// По возвращении из метода это уже другая ссылка!
// Под именем xxx – другой объект.

 }//======================================
}//=============================================
Листинг 3.4.

Ссылка и ссылка на ссылку как параметры

На самом деле ВСЕ параметры ВСЕГДА передаются по значению. Это означает, что в области активации создается копия ЗНАЧЕНИЯ параметра. При этом важно, ЧТО копируется в эту область. Для ссылочных типов возможны два варианта:

  • можно копировать адрес объекта (ссылка как параметр),
  • можно копировать адрес переменной, которая указывает на объект (ссылка на ссылку как параметр).

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

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

Следующий программный код демонстрирует специфику передачи параметров:

using System;
namespace Ref_RefRef
{

// Ссылка и ссылка на ссылку.
class WorkClass
{
public int x;

// Варианты конструкторов.
public WorkClass()
 {
x = 0;
 }

public WorkClass(int key)
 {
x = key;
 }

public WorkClass(WorkClass wKey)
 {
x = wKey.x;
 }
} 
	
class ClassRef
{
static void Main(string[] args)
 {
WorkClass w0 = new WorkClass();
Console.WriteLine("on start: {0}", w0.x); //_: 0
f0(w0);
Console.WriteLine("after f0: {0}", w0.x); //0: 1
f1(w0);
Console.WriteLine("after f1: {0}", w0.x); //1: 1
f2(ref w0);
Console.WriteLine("after f2: {0}", w0.x); //2: 10
f3(ref w0);
Console.WriteLine("after f3: {0}", w0.x); //3: 3

// Еще один объект...
WorkClass w1 = new WorkClass(w0);
ff(w0, ref w1);

if (w0 == null) Console.WriteLine("w0 == null");
else Console.WriteLine("w0 != null"); // !!!

if (w1 == null) Console.WriteLine("w1 == null"); // !!!
else Console.WriteLine("w1 != null");

}
static void f0(WorkClass wKey)
 {
wKey.x = 1;
Console.WriteLine(" in f0: {0}", wKey.x);
 }

static void f1(WorkClass wKey)
 {
wKey = new WorkClass(2); 
Console.WriteLine(" in f1: {0}", wKey.x);
 }

static void f2(ref WorkClass wKey)
 {
wKey.x = 10;
Console.WriteLine(" in f2: {0}", wKey.x);
 }

static void f3(ref WorkClass wKey)
 {
wKey = new WorkClass(3);
Console.WriteLine(" in f3: {0}", wKey.x);
 }

static void ff(WorkClass wKey, ref WorkClass refKey)
 {
wKey = null;
refKey = null;
 }

 }
}
Листинг 3.5.

В результате выполнения программы в окошко консольного приложения выводится информация следующего содержания:

on start: 0
in f0: 1
after f0: 1
in f1: 2
after f1: 1
in f2: 10
after f2: 10
in f3: 3
after f3: 3
w0 != null
w1 == null
< Лекция 2 || Лекция 3: 123456 || Лекция 4 >
kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2