Опубликован: 25.03.2010 | Уровень: для всех | Доступ: платный
Лекция 7:

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

< Лекция 6 || Лекция 7: 12345 || Лекция 8 >

Статические члены класса


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

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

Таким образом, обычные данные-члены и обычные методы вызываются по имени экземпляра, а статические данные и методы вызываются по имени класса.В связи с этим, статические методы не могут использовать обычные поля и обычные методы, ибо последние начинают существовать только после создания экземпляра. Но обычные методы могут использовать статические данные и статические методы, ибо последние начинают существовать сразу после загрузки программы.

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

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

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

using System;
    
class Bones // Игра в кости
{
    // Объявление статического поля
    private static Random rnd = new Random();
    
    // Объявление и определение статических методов
    private static int GetRandomNumber(short maxValue)
    {
        return rnd.Next(maxValue);
    }
        
    public static string Throw()// Бросок 
    {
    string[] messages = new string[6] { "Единица", 
                                        "Двойка", 
                                        "Тройка", 
                                        "Четверка", 
                                        "Пятерка", 
                                        "Шестерка" };
    return messages[GetRandomNumber(6)];
    }
}
    
class Start
{
    static void Main()
    {
        // Выводим броски
        for (int i = 0; i < 10; i++)
            Console.WriteLine("{0}) {1}", i + 1, Bones.Throw());
    
        Console.ReadLine();
    }
}
Листинг 7.9. Генерация бросков нумерованного шестигранного куба на уровне класса

Метод Throw() объявлен статическим и использует вспомогательную функцию GetRandomNumber(), которая в свою очередь использует поле rnd. Поэтому все вспомогательные члены должны быть статическими. Попробуйте убрать слово static из объявления поля rnd или метода GetRandomNumber() - компилятор сразу выдаст ошибку.

Если бы метод Throw() не был объявлен как static, то нужно было бы создать экземпляр класса Bones и вызывать метод на объектном уровне. При этом поле rnd и вспомогательный метод GetRandomNumber() можно оставить статическими

using System;
    
class Bones // Игра в кости
{
    // Объявление статического поля
    private static Random rnd = new Random();
    
    // Объявление и определение статических методов
    private static int GetRandomNumber(short maxValue)
    {
        return rnd.Next(maxValue);
    }
        
    public string Throw()// Бросок 
    {
    string[] messages = new string[6] { "Единица", 
                                        "Двойка", 
                                        "Тройка", 
                                        "Четверка", 
                                        "Пятерка", 
                                        "Шестерка" };
    return messages[GetRandomNumber(6)];
    }
}
    
class Start
{
    static void Main()
    {
        // Создаем экземпляр класса (объект)
        Bones cube = new Bones();
    
        // Выводим броски
        for (int i = 0; i < 10; i++)
            Console.WriteLine("{0}) {1}", i + 1, cube.Throw());
    
        Console.ReadLine();
    }
}
Листинг 7.10. Генерация бросков нумерованного шестигранного куба на уровне объекта

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

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

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

Приведем пример

using System;
    
class Student // Модель одного студента
{
    // Члены уровня класса
    private static string institute = "КИЦМ"; // Институт
    public static void ChangeInstitute(string ins)
    { institute = ins; }
    private static string faculty = "Горный"; // Факультет
    public static void ChangeFaculty(string fac)
    { faculty = fac; }
    
    // Члены объектного уровня
    private string surName; // Фамилия
    private int age, rate;  // Возраст, курс
    
    // Конструктор
    public Student(string surName, int age, int rate)
    {
        this.surName = surName;
        this.age = age;
        this.rate = rate;
    }
    
    // Метод печати всей информации о студенте
    public void Show()
    {
        Console.WriteLine("Студент {0}: ", surName);
        Console.Write("Институт " + institute
                    + ", факультет " + faculty);
        Console.WriteLine(", возраст " + age
                        + ", курс " + rate);
        Console.WriteLine();    // Создание пустой строки
    }
}
    
class Start
{
    static void Main()
    {
        // Настройка консоли
        Console.Title = "Статические и объектные члены";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        // Создаем массив ссылок
        Student[] students = new Student[3];
    
        // Создаем несколько студентов
        students[0] = new Student("Иванов", 18, 1);
        students[1] = new Student("Петров", 19, 2);
        students[2] = new Student("Сидоров", 20, 3);
    
        // Выводим студентов до реорганизации
        Console.WriteLine("Студенты до реорганизации:");
        for (int i = 0; i < students.Length; i++)
            students[i].Show();
    
        // Меняем общее для всех студентов 
        // название института и факультета
        Student.ChangeInstitute("СФУ");
        Student.ChangeFaculty("ФФО");
    
        // Выводим студентов после реорганизации
        Console.WriteLine(Environment.NewLine + "Студенты 
          после реорганизации:");
        for (int i = 0; i < students.Length; i++)
            students[i].Show();
    
        Console.ReadLine();
    }
}
Листинг 7.11. Совместное использование общих и индивидуальных данных

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

< Лекция 6 || Лекция 7: 12345 || Лекция 8 >
Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

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

 

Как активировать код?

Денис Пашков
Денис Пашков
Россия
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974