Шаг 137.
Язык программирования C#. Начала
Свойства и индексаторы. Примеры использования
На этом шаге мы рассмотрим несколько примеров решения задач, в которых используются свойства и индексаторы.
Здесь мы рассмотрим несколько программ, в которых используются ранее рассмотренные свойства и ииндексаторы.
Задание 1. Напишите программу, в которой есть класс с целочисленным массивом и целочисленным свойством. При считывании значения свойства оно последовательно и
циклически возвращает значения элементов массива. При присваивании значения свойству изменяется значение того элемента, который в данный момент интерпретируется как значение свойства.
Раскрыть/скрыть решение и комментарии.
Вот текст программы, реализующий указанную задачу.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr137_1
{
// Класс со свойством:
class MyClass {
// Закрытые целочисленные поля:
private int tekNum; // Текущий номер элемента
private int kol; // Количество элементов массива
private int[] mas; // Массив
// Конструктор с двумя аргументами:
public MyClass(int[] t) {
// Присваивание значения полям:
tekNum = 0;
kol = t.Length;
mas = t;
}
// Описание целочисленного свойства:
public int code{
// Метод вызывается при считывании значения свойства:
get{
// Значение свойства:
int t = mas[tekNum]; // Определяем значение текущего элемента
tekNum = ++tekNum % kol; // Вычисляем следующий номер
return t; // Возвращаем значение текущего элемента
}
// Метод вызывается при присваивании
// значения свойству:
set{
mas[tekNum] = value; // Меняем значение текущего элемента
}
}
}
// Класс с главным методом:
class Program
{
static void Main()
{
// Создание и инициализация массива:
int[] nums = { 1, 3, 5, 7, 6, 5, 4 };
// Создание объекта:
MyClass obj = new MyClass(nums);
// Проверка содержимого объекта:
Console.WriteLine("10 раз вывели свойство code: ");
for (int i = 0; i < 10; i++)
Console.WriteLine(obj.code);
Console.WriteLine("Присвоили свойству code значение -7.");
obj.code = -7;
Console.WriteLine("10 раз вывели свойство code: ");
for (int i = 0; i < 10; i++)
Console.WriteLine(obj.code);
// Задержка:
Console.ReadLine();
}
}
}
Архив проекта можно взять
здесь.
Результат работы приложения приведен на рисунке 1.
Рис.1. Результат работы приложения
Прокомментируем приведенный текст программы.
В основной функции определен целочисленный массив
int[] nums = { 1, 3, 5, 7, 6, 5, 4 }; ,
который передается в качестве параметра в конструктор при создании объекта
obj. Затем выводится 10 раз значение свойства
code. Так как реализовано циклическое обращение к элементам массива,
то последним выведенным элементом будет 5, и текущим элементом (элементом, к которому будет реализовано обращение) становится элемент массива со значением 7.
Затем выполняется оператор
меняющий это значение на -7 (этот элемент по прежнему является текущим), после чего снова 10 раз выводится значение свойства
code.
Задание 2. Напишите программу с классом, у которого есть неотрицательное целочисленное поле. В классе нужно описать индексатор с целочисленным индексом и
set-аксессором. Присваивание значения проиндексированному объекту обрабатывается следующим образом. В фактически присваиваемом значении берется только последняя цифра
(остаток от деления числа на 10). Индекс определяет разряд в числовом значении поля, в который записывается цифра. Нулевой разряд соответствует единицам, единичный разряд соответствует
десяткам, разряд два соответствует сотням и так далее. Например, если объект проиндексирован числом 1 и присваивается значение, заканчивающееся на 5, то это означает, что в числе, которое
является значением поля, в разряд десятков (разряд 1) нужно записать цифру 5.
Раскрыть/скрыть решение и комментарии.
Вот программа, решающая указанную задачу:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr137_2
{
// Класс с индексатором:
class MyClass
{
// Закрытое числовое поле:
private int num;
// Конструктор с текстовым аргументом:
public MyClass(int t)
{
num = t;
}
// Переопределение метода ToString():
public override string ToString()
{
// Текстовая переменная:
string txt = "Содержимое объекта: " + num;
// Результат метода:
return txt;
}
// Числовой индексатор с целочисленным индексом:
public int this [ int k]
{
// Метод вызывается при присваивании значения
// выражению с индексированным объектом:
set
{
int res = value % 10;
// Проверка значения индекса:
if (k < 0 || k >= (num.ToString()).Length) return;
// Числовая переменная:
int t = 0;
int ch = num;
// Добавление цифр к числу:
for (int i = 0; i < k; i++)
{
t += (ch % 10) * Convert.ToInt32(Math.Pow(10, i));
ch /= 10;
}
// Добавление к числу нужной цифры:
t += res * Convert.ToInt32(Math.Pow(10, k));
ch /= 10;
// Добавление цифр к числу:
for (int i = k + 1; i < (num.ToString()).Length; i++)
{
t += (ch % 10) * Convert.ToInt32(Math.Pow(10, i));
ch /= 10;
}
// Новое значение числа:
num = t;
}
}
}
// Класс с главным методом:
class Program
{
// Главный метод:
static void Main()
{
// Создание объекта:
MyClass obj = new MyClass(357);
// Проверка содержимого объекта:
Console.WriteLine(obj);
// Попытка изменить цифру в числе:
obj[-1] = 111;
// Проверка содержимого объекта:
Console.WriteLine(obj);
// Попытка изменить цифру в числе:
obj[8] = 88;
// Проверка содержимого объекта:
Console.WriteLine(obj);
// Изменение цифры в числе:
obj[0] = 999;
// Проверка содержимого объекта:
Console.WriteLine(obj);
// Изменение цифры в числе:
obj[2] = 776;
// Проверка содержимого объекта:
Console.WriteLine(obj);
// Изменение цифры в числе:
obj[1] = 34;
// Проверка содержимого объекта:
Console.WriteLine(obj);
// Задержка:
Console.ReadLine();
}
}
}
Архив проекта можно взять
здесь.
Результат работы приложения изображен на рисунке 2.
Рис.2. Результат работы приложения
Основная сложность приложения - реализация set-акссесора в числовом индексаторе. Дадим краткие комментарии к его реализации.
Параметр k определяет заменяемый разряд, поэтому конструкцией
if (k < 0 || k >= (num.ToString()).Length) return;
проверяем существование такого разряда. Переменная
res содержит последнюю цифру указанного значения, то есть цифру, которая должна "встать" в указанный параметром
k разряд.
Затем формируем новое значение, которое будет находиться в переменной t. Сначала без изменений берем все цифры из разрядов, меньших k:
// Добавление цифр к числу:
for (int i = 0; i < k; i++)
{
t += (ch % 10) * Convert.ToInt32(Math.Pow(10, i));
ch /= 10;
}
Здесь получаем очередную последнюю цифру (ch % 10), которую затем умножаем на 10 в стапени, равной номеру разряда Convert.ToInt32(Math.Pow(10, k)), предварительно преобразовав
это значение к целому типу (Convert.ToInt32()). Полученное произведение прибавляем к t, после чего отбрасываем последнюю цифру числа ch.
Когда доходим до k-го разряда, добавляем цифру res к числу t
t += res * Convert.ToInt32(Math.Pow(10, k));
ch /= 10;
не забывая при этом отбросить последнюю цифру числа
ch, после чего процесс повторяем для разрядов, больших
k:
for (int i = k + 1; i < (num.ToString()).Length; i++)
{
t += (ch % 10) * Convert.ToInt32(Math.Pow(10, i));
ch /= 10;
}
Полученное значение будет значением свойства num
Задание 3. Напишите программу с классом, в котором есть текстовый массив. Опишите в классе одномерный и двумерный индексаторы. Одномерный индексатор позволяет
прочитать элемент текстового массива и присвоить новое значение элементу текстового массива. Двумерный индексатор позволяет прочитать символ в элементе текстового массива (первый индекс
определяет элемент в текстовом массиве, а второй индекс определяет символ в тексте). Предусмотрите циклическую перестановку индексов в случае, если они выходят за верхнюю допустимую границу.
Раскрыть/скрыть решение и комментарии.
Программа, решающая эту задачу, приведена ниже:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr137_3
{
// Класс с несколькими индексаторами:
class MyClass
{
// Закрытый строковый массив:
private string[] text;
// Конструктор класса с одним аргументом:
public MyClass(string[] t)
{
// Создание массива:
text = new string[t.Length];
// Заполнение массива:
for (int k = 0; k < t.Length; k++)
{
// Использование индексатора:
text[k] = t[k];
}
}
// Переопределение метода ToString():
public override string ToString()
{
// Текстовая переменная:
string txt = "Содержимое объекта:\n";
// Формирование текстовой строки:
for (int k = 0; k < text.Length; k++)
{
txt += "\"" + text[k] + "\"" + (k == text.Length - 1 ? "\n" : " ");
}
// Результат метода:
return txt;
}
// Индексатор с целочисленным индексом:
public string this[int k]
{
// Аксессор для считывания значения:
get
{
return text[k];
}
// Аксессор для присваивания значения:
set
{
if (k < 0 || k >= text.Length) return;
text[k] = value;
}
}
// Индексатор с двумя целочисленными индексами:
public string this[int k, int l]
{
// Аксессор для считывания значения:
get
{
// Циклическая перестановка:
if (k < 0 || k >= text.Length)
{
int temp = k;
k = l;
l = temp;
}
// Циклическая перестановка:
if (l < 0 || l >= text[k].Length)
{
int temp = k;
k = l;
l = temp;
}
// Возвращаемый символ:
return text[k].Substring(l, 1);
}
}
}
// Класс с главным методом:
class Program
{
// Главный метод:
static void Main()
{
string[] t = {"мама", "мыла", "раму", "с", "мылом"};
MyClass obj = new MyClass(t);
// Проверка содержимого объекта:
Console.WriteLine(obj);
// Использование одномерного индексатора:
obj[0] = "папа";
obj[1] = "мыл";
// Проверка использования одномерного индексатора:
Console.WriteLine("obj[{0}] = {1}", 0, obj[0]);
Console.WriteLine("obj[{0}] = {1}", 1, obj[1]);
// Проверка содержимого объекта:
Console.WriteLine("\n" + obj);
// Проверка использования двумерного индексатора:
Console.WriteLine("obj[{0}, {1}] = {2}", 0, 2, obj[0, 2]);
// Проверка циклической перестановки индексов
Console.WriteLine("obj[{0}, {1}] = {2}", 2, 4, obj[2, 4]);
// Проверка содержимого объекта:
Console.WriteLine("\n" + obj);
// Задержка:
Console.ReadLine();
}
}
}
Архив проекта можно взять
здесь.
Результат работы приложения изображен на рисунке 3.
Рис.3. Результат работы приложения
Попробуйте разобраться с текстом программы самостоятельно. Попытайтесь объяснить результат, выводимый при использовании конструкции
Console.WriteLine("obj[{0}, {1}] = {2}", 2, 4, obj[2, 4]);
Со следующего шага мы начнем знакомиться с наследованием.
Предыдущий шаг
Содержание
Следующий шаг