Разработка приложений на C# в среде Visual Studio 2005

Основы языка C#. Часть 2

Разбить на страницы
Показывать лекцию целиком

Модификаторы параметров методов

В языке C# существуют два типа переменных - типы, характеризуемые значением и ссылочные типы. Переменные типа значения содержат именованные данные, к которым относятся все числовые типы, структуры и перечисления. Они создаются при компиляции программы и размещаются в стековой области памяти при их загрузке. Ссылочные типы содержат адреса участков памяти, создаваемых динамически при выполнении программы, и представляют неименованные данные.

Объекты типа значения и ссылки
Объявление типа Создание объекта Альтернативный синтаксис создания объекта
Встроенный тип, характеризуемый значением int a; int a = new int();
Пользовательский тип, характеризуемый значением struct MyStruct { public x, y; } MyStruct point; MyStruct point = new MyStruct();
Пользовательский тип, характеризуемый ссылкой
struct MyClass
{
    public x, y;
}
// Объявили ссылку 
MyClass point; 
// Создали и привязали объект 
point = new MyClass();
MyClass point = new MyClass();

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

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

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

Модификаторы параметров в C#
Модификатор Описание
(нет) Если параметр метода не помечен модификатором, то предполагается передача параметра по значению, т.е. в вызываемый метод передается копия оригинальных данных
out Выходной параметр устанавливается вызываемым методом и передается по ссылке. Если вызываемый метод не присвоит выходному параметру значение, то генерируется ошибка компиляции
params Позволяет переслать произвольное число аргументов одинакового типа в виде единого параметра. Для любого метода допускается только один модификатор params и только для последнего по порядку следования параметра
ref Передается ссылка на данные клиента, которые метод может менять внутри себя. Фактически метод работает с данными, размещенными вне его

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

Входные параметры, представленные значением

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

Приведем простой пример.

using System;
    
static class Calculator
{
    public static int Add(int x, int y, int a, int b)
    {
        int answer = x + y;
        x = 10; y = 20; a = 30; b = 40;
        return answer;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Передача параметров по значению";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        int x = 1, y = 2, a = 3, b = 4;
        Console.WriteLine("До вызова: \tx={0}; y={1}; a={2}; b={3}",
            x, y, a, b);
        Calculator.Add(x, y, a, b);
        Console.WriteLine("После вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
    
        Console.ReadLine();
    }
}

Результат будет таким

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

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

Выходные параметры, представленные значением

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

Пометим в предыдущем примере два параметра как выходные. Код будет таким

using System;
    
static class Calculator
{
    public static int Add(int x, int y, out int a, out int b)
    {
        int answer = x + y;
        x = 10; y = 20; a = 30; b = 40;
        return answer;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Применение выходных параметров";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        int x = 1, y = 2, a = 3, b = 4;
        Console.WriteLine("До вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
        Calculator.Add(x, y, out a, out b);
        Console.WriteLine("После вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
    
        Console.ReadLine();
    }
}

Выполним пример

Мы видим, что отмеченные как выходные параметры меняют в вызывающем коде свои значения. Им в клиенте не обязательно присваивать значения, все равно метод их изменит.

Ссылочные параметры, определенные явно

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

Добавим в предыдущем примере в объявление и вызов первых двух параметров метода Add() модификатор ref ( reference - ссылка), заставляющий компилятор включить механизм передачи этих параметров по ссылке. Код станет таким

using System;
    
static class Calculator
{
    public static int Add(ref int x, ref int y, out int a, out int b)
    {
        int answer = x + y;
        x = 10; y = 20; a = 30; b = 40;
        return answer;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Передача параметров по ссылке";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        int x = 1, y = 2, a = 3, b = 4;
        Console.WriteLine("До вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
        Calculator.Add(ref x, ref y, out a, out b);
        Console.WriteLine("После вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
    
        Console.ReadLine();
    }
}

Получим результат

Метод внутри себя меняет значения данных клиента, переданных по ссылке.

Ссылочные параметры, определенные неявно

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

using System;
    
class MyClass
{
    // Открытые поля
    public int a, b;
    
    // Конструктор
    public MyClass(int a, int b)
    {
        this.a = a;
        this.b = b;
    }
    
    // Печать
    public void Show()
    {
        Console.WriteLine("a={0}, b={1}", a, b);
    }
}
    
class Start
{
    static void ChangeObject(MyClass ob)
    {
        ob.a = 10;
        ob.b = 20;
    
        ob = null; // Изменяем копию ссылки
    }
    
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Передача методу объекта ссылочного типа";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        // Создаем объект
        MyClass ob1 = new MyClass(1, 2);
    
        Console.Write("До вызова: \t"); ob1.Show();
    
        // Передаем методу объект в виде ссылки 
        ChangeObject(ob1);
    
        Console.Write("После вызова: \t"); ob1.Show();
    
        Console.ReadLine();
    }
}

Результат будет таким

Если в метод передать ссылку на объект с модификатором ref, тогда в методе окажется адресная переменная самой ссылки (ссылка на ссылку) и смена значения этой адресной переменной изменит значение ссылки, что приведет к потере объекта или к адресации на новый объект.

using System;
    
static class Swap
{
    public static void SwapRef(ref string str1, ref string str2)
    {
        string tmp = str1;
        str1 = str2;
        str2 = tmp;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройки консоли
        Console.Title = "Обмен строк через адреса";
        Console.ForegroundColor = ConsoleColor.White; // Белый текст
        Console.WindowWidth = 45;// Знакомест клиентской области
        Console.WindowHeight = 3;// Строк клиентской области
        Console.CursorVisible = false;
    
        // Вызывающий код
        string string1 = "Первая строка";
        string string2 = "Вторая строка";
        Console.WriteLine("До вызова: \t{0}, 
          {1}", string1, string2);
        Swap.SwapRef(ref string1, ref string2);
        Console.WriteLine("После вызова: \t{0}, 
          {1}", string1, string2);
    
        Console.ReadLine();
    }
}

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

Результат выполнения будет таким

Вот еще один пример.

using System;
using System.IO;
using System.Text;
    
class MyClass
{
    // Печать в свойство, базового поля нет
    public String Result
    {
        get
        {
            return String.Format("a={0}, b={1}", a, b);
        }
    }
    
    // Закрытые поля
    int a, b;
    
    // Конструктор
    public MyClass(int a, int b)
    {
        this.a = a;
        this.b = b;
    }
    
    public static void SwapRef(ref MyClass ob1, 
      ref MyClass ob2, ref MyClass obNew)
    {
        MyClass tmp;// Объявляем вспомогательную ссылку
    
        // Обмениваем значения ссылок
        tmp = ob1;
        ob1 = ob2;
        ob2 = tmp;
    
        // Создаем новый объект и прикрепляем к ссылке клиента
        obNew = new MyClass(5, 6);
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройки консоли
        Console.Title = "Обмен объектов через адреса";
        Console.ForegroundColor = ConsoleColor.White; // Белый текст
        Console.CursorVisible = false;
    
        // Создаем и открываем поток вывода в файл
        StreamWriter writeFile =
            new StreamWriter(@"C:\1\xx.txt", false, 
            ASCIIEncoding.GetEncoding(1251));
    
        // Вызывающий код
        // Создаем объекты
        MyClass ob1 = new MyClass(1, 2);
        MyClass ob2 = new MyClass(3, 4);
    
        // Создаем ссылку без объекта
        MyClass ob3 = null;
    
        writeFile.WriteLine("До вызова: ");
        writeFile.WriteLine(ob1.Result);
        writeFile.WriteLine(ob2.Result);
        writeFile.WriteLine();
    
        // Передаем методу объект в виде ссылки 
        MyClass.SwapRef(ref ob1, ref ob2, ref ob3);
    
        writeFile.WriteLine("После вызова: ");
        writeFile.WriteLine(ob1.Result);
        writeFile.WriteLine(ob2.Result);
        writeFile.WriteLine(ob3.Result);
    
        writeFile.Close();
    }
}

Результат выполнения будет размещен в автоматически созданном текстовом файле с нашим именем xx.txt и будет таким

До вызова: 
a=1, b=2
a=3, b=4

После вызова: 
a=3, b=4
a=1, b=2
a=5, b=6

В последнем приведенном коде мы немножко отвлеклись от темы и сделали вывод результатов в текстовый файл, чтобы не приводить снимки экрана, которые занимают больше места в данном описании, чем текст. Для этого потребовались дополнительные пространства имен: System.IO и System.Text. Для простоты мы выборку значений внутренних полей класса выполнили через строковое свойство, а для вывода в поток воспользовались методом WriteLine() класса System.IO.StreamWriter. В дальнейшем иногда мы будем придерживаться именно такого подхода, чтобы иметь возможность копировать результаты в текстовом виде.

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

Project/Имя_проекта Properties/Application/Output type/Windows Application

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

Передача методу переменного количества аргументов

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

Рассмотрим пример, в котором ищется минимум из произвольного числа аргументов

using System;
using System.IO;
using System.Text;
    
class Min
{
    // Печать в статическое свойство
    static String result;
    public static String Result
    {
        get
        {
            return result;
        }
    }
     
    public static void MinValue(params int[] nums)
    {
        if (nums.Length == 0)
        {
            result = "Нет аргументов";
            return;
        }
    
        int min = nums[0];
        for (int i = 1; i < nums.Length; i++)
            if (nums[i] < min)
                min = nums[i];
    
        result = "Минимум равен " + min;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Создаем и открываем поток вывода в файл
        StreamWriter writeFile =
            new StreamWriter(@"C:\1\xx.txt", false, ASCIIEncoding.
                    GetEncoding(1251));
    
        // Вызывающий код
        Min.MinValue(10, 20, 30, 40, 50);
        writeFile.WriteLine(Min.Result);
        Min.MinValue();
        writeFile.WriteLine(Min.Result);
    
        writeFile.Close();
    }
}

Результат выполнения

Минимум равен 10
Нет аргументов
Страницы:

Модификаторы параметров методов

В языке C# существуют два типа переменных - типы, характеризуемые значением и ссылочные типы. Переменные типа значения содержат именованные данные, к которым относятся все числовые типы, структуры и перечисления. Они создаются при компиляции программы и размещаются в стековой области памяти при их загрузке. Ссылочные типы содержат адреса участков памяти, создаваемых динамически при выполнении программы, и представляют неименованные данные.

Объекты типа значения и ссылки
Объявление типа Создание объекта Альтернативный синтаксис создания объекта
Встроенный тип, характеризуемый значением int a; int a = new int();
Пользовательский тип, характеризуемый значением struct MyStruct { public x, y; } MyStruct point; MyStruct point = new MyStruct();
Пользовательский тип, характеризуемый ссылкой
struct MyClass
{
    public x, y;
}
// Объявили ссылку 
MyClass point; 
// Создали и привязали объект 
point = new MyClass();
MyClass point = new MyClass();

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

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

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

Модификаторы параметров в C#
Модификатор Описание
(нет) Если параметр метода не помечен модификатором, то предполагается передача параметра по значению, т.е. в вызываемый метод передается копия оригинальных данных
out Выходной параметр устанавливается вызываемым методом и передается по ссылке. Если вызываемый метод не присвоит выходному параметру значение, то генерируется ошибка компиляции
params Позволяет переслать произвольное число аргументов одинакового типа в виде единого параметра. Для любого метода допускается только один модификатор params и только для последнего по порядку следования параметра
ref Передается ссылка на данные клиента, которые метод может менять внутри себя. Фактически метод работает с данными, размещенными вне его

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

Входные параметры, представленные значением

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

Приведем простой пример.

using System;
    
static class Calculator
{
    public static int Add(int x, int y, int a, int b)
    {
        int answer = x + y;
        x = 10; y = 20; a = 30; b = 40;
        return answer;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Передача параметров по значению";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        int x = 1, y = 2, a = 3, b = 4;
        Console.WriteLine("До вызова: \tx={0}; y={1}; a={2}; b={3}",
            x, y, a, b);
        Calculator.Add(x, y, a, b);
        Console.WriteLine("После вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
    
        Console.ReadLine();
    }
}

Результат будет таким

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

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

Выходные параметры, представленные значением

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

Пометим в предыдущем примере два параметра как выходные. Код будет таким

using System;
    
static class Calculator
{
    public static int Add(int x, int y, out int a, out int b)
    {
        int answer = x + y;
        x = 10; y = 20; a = 30; b = 40;
        return answer;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Применение выходных параметров";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        int x = 1, y = 2, a = 3, b = 4;
        Console.WriteLine("До вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
        Calculator.Add(x, y, out a, out b);
        Console.WriteLine("После вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
    
        Console.ReadLine();
    }
}

Выполним пример

Мы видим, что отмеченные как выходные параметры меняют в вызывающем коде свои значения. Им в клиенте не обязательно присваивать значения, все равно метод их изменит.

Ссылочные параметры, определенные явно

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

Добавим в предыдущем примере в объявление и вызов первых двух параметров метода Add() модификатор ref ( reference - ссылка), заставляющий компилятор включить механизм передачи этих параметров по ссылке. Код станет таким

using System;
    
static class Calculator
{
    public static int Add(ref int x, ref int y, out int a, out int b)
    {
        int answer = x + y;
        x = 10; y = 20; a = 30; b = 40;
        return answer;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Передача параметров по ссылке";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        int x = 1, y = 2, a = 3, b = 4;
        Console.WriteLine("До вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
        Calculator.Add(ref x, ref y, out a, out b);
        Console.WriteLine("После вызова: \tx={0}; 
          y={1}; a={2}; b={3}",
            x, y, a, b);
    
        Console.ReadLine();
    }
}

Получим результат

Метод внутри себя меняет значения данных клиента, переданных по ссылке.

Ссылочные параметры, определенные неявно

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

using System;
    
class MyClass
{
    // Открытые поля
    public int a, b;
    
    // Конструктор
    public MyClass(int a, int b)
    {
        this.a = a;
        this.b = b;
    }
    
    // Печать
    public void Show()
    {
        Console.WriteLine("a={0}, b={1}", a, b);
    }
}
    
class Start
{
    static void ChangeObject(MyClass ob)
    {
        ob.a = 10;
        ob.b = 20;
    
        ob = null; // Изменяем копию ссылки
    }
    
    static void Main()// Вызывающая сторона
    {
        // Настройка консоли
        Console.Title = "Передача методу объекта ссылочного типа";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        // Создаем объект
        MyClass ob1 = new MyClass(1, 2);
    
        Console.Write("До вызова: \t"); ob1.Show();
    
        // Передаем методу объект в виде ссылки 
        ChangeObject(ob1);
    
        Console.Write("После вызова: \t"); ob1.Show();
    
        Console.ReadLine();
    }
}

Результат будет таким

Если в метод передать ссылку на объект с модификатором ref, тогда в методе окажется адресная переменная самой ссылки (ссылка на ссылку) и смена значения этой адресной переменной изменит значение ссылки, что приведет к потере объекта или к адресации на новый объект.

using System;
    
static class Swap
{
    public static void SwapRef(ref string str1, ref string str2)
    {
        string tmp = str1;
        str1 = str2;
        str2 = tmp;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройки консоли
        Console.Title = "Обмен строк через адреса";
        Console.ForegroundColor = ConsoleColor.White; // Белый текст
        Console.WindowWidth = 45;// Знакомест клиентской области
        Console.WindowHeight = 3;// Строк клиентской области
        Console.CursorVisible = false;
    
        // Вызывающий код
        string string1 = "Первая строка";
        string string2 = "Вторая строка";
        Console.WriteLine("До вызова: \t{0}, 
          {1}", string1, string2);
        Swap.SwapRef(ref string1, ref string2);
        Console.WriteLine("После вызова: \t{0}, 
          {1}", string1, string2);
    
        Console.ReadLine();
    }
}

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

Результат выполнения будет таким

Вот еще один пример.

using System;
using System.IO;
using System.Text;
    
class MyClass
{
    // Печать в свойство, базового поля нет
    public String Result
    {
        get
        {
            return String.Format("a={0}, b={1}", a, b);
        }
    }
    
    // Закрытые поля
    int a, b;
    
    // Конструктор
    public MyClass(int a, int b)
    {
        this.a = a;
        this.b = b;
    }
    
    public static void SwapRef(ref MyClass ob1, 
      ref MyClass ob2, ref MyClass obNew)
    {
        MyClass tmp;// Объявляем вспомогательную ссылку
    
        // Обмениваем значения ссылок
        tmp = ob1;
        ob1 = ob2;
        ob2 = tmp;
    
        // Создаем новый объект и прикрепляем к ссылке клиента
        obNew = new MyClass(5, 6);
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Настройки консоли
        Console.Title = "Обмен объектов через адреса";
        Console.ForegroundColor = ConsoleColor.White; // Белый текст
        Console.CursorVisible = false;
    
        // Создаем и открываем поток вывода в файл
        StreamWriter writeFile =
            new StreamWriter(@"C:\1\xx.txt", false, 
            ASCIIEncoding.GetEncoding(1251));
    
        // Вызывающий код
        // Создаем объекты
        MyClass ob1 = new MyClass(1, 2);
        MyClass ob2 = new MyClass(3, 4);
    
        // Создаем ссылку без объекта
        MyClass ob3 = null;
    
        writeFile.WriteLine("До вызова: ");
        writeFile.WriteLine(ob1.Result);
        writeFile.WriteLine(ob2.Result);
        writeFile.WriteLine();
    
        // Передаем методу объект в виде ссылки 
        MyClass.SwapRef(ref ob1, ref ob2, ref ob3);
    
        writeFile.WriteLine("После вызова: ");
        writeFile.WriteLine(ob1.Result);
        writeFile.WriteLine(ob2.Result);
        writeFile.WriteLine(ob3.Result);
    
        writeFile.Close();
    }
}

Результат выполнения будет размещен в автоматически созданном текстовом файле с нашим именем xx.txt и будет таким

До вызова: 
a=1, b=2
a=3, b=4

После вызова: 
a=3, b=4
a=1, b=2
a=5, b=6

В последнем приведенном коде мы немножко отвлеклись от темы и сделали вывод результатов в текстовый файл, чтобы не приводить снимки экрана, которые занимают больше места в данном описании, чем текст. Для этого потребовались дополнительные пространства имен: System.IO и System.Text. Для простоты мы выборку значений внутренних полей класса выполнили через строковое свойство, а для вывода в поток воспользовались методом WriteLine() класса System.IO.StreamWriter. В дальнейшем иногда мы будем придерживаться именно такого подхода, чтобы иметь возможность копировать результаты в текстовом виде.

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

Project/Имя_проекта Properties/Application/Output type/Windows Application

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

Передача методу переменного количества аргументов

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

Рассмотрим пример, в котором ищется минимум из произвольного числа аргументов

using System;
using System.IO;
using System.Text;
    
class Min
{
    // Печать в статическое свойство
    static String result;
    public static String Result
    {
        get
        {
            return result;
        }
    }
     
    public static void MinValue(params int[] nums)
    {
        if (nums.Length == 0)
        {
            result = "Нет аргументов";
            return;
        }
    
        int min = nums[0];
        for (int i = 1; i < nums.Length; i++)
            if (nums[i] < min)
                min = nums[i];
    
        result = "Минимум равен " + min;
    }
}
    
class Start
{
    static void Main()// Вызывающая сторона
    {
        // Создаем и открываем поток вывода в файл
        StreamWriter writeFile =
            new StreamWriter(@"C:\1\xx.txt", false, ASCIIEncoding.
                    GetEncoding(1251));
    
        // Вызывающий код
        Min.MinValue(10, 20, 30, 40, 50);
        writeFile.WriteLine(Min.Result);
        Min.MinValue();
        writeFile.WriteLine(Min.Result);
    
        writeFile.Close();
    }
}

Результат выполнения

Минимум равен 10
Нет аргументов
Вернуться к учебному плану