Методичка по C (987695), страница 5
Текст из файла (страница 5)
return my;
}
static void Main(string[] args)
{
double [,]arr;
arr=new double[5,3];
mas1 m1=new mas1(5);// экземпляр структуры для представления данных
for(int i=0;i<5;i++)
{for(int j=0;j<3;j++)
{arr[i,j]=(2+i)*(j+4);
Console.Write("Rida ["+i+","+j+"]="+arr[i,j]+" ");}
Console.WriteLine();}
m1=sum11(arr);
for(int i=0;i<5;i++)
Console.WriteLine("Summa "+i+" on "+m1[i]);
// обращение m1[i] разрешено только благодаря индексатору
Console.ReadLine();
} } }
3.5. Свойства
Свойства – это обобщение понятия данных. Значения свойств можно вычислить на основе значений данных, а также через свойство можно присвоить значения данным. Часто свойство вычисляют на базе нескольких или даже целого множества данных (например, свойство треугольника периметр – можно вычислить, зная его стороны; свойство – сумма элементов массива – на основе . . . ). В таком случае, естественно, вопрос о вычислении данных на основе значения свойства бессмысленный, так как эта задача не решается однозначно.
Определение свойства:
Тип_данных_свойства имя_свойства
{
get {
//получение значения свойства
}
set {
// использование переданного значения свойства
} }
Пример на использование свойств.
namespace ConApp7
{
class Prop1
{
double[] mas;
double lim;
public Prop1()
{
int n;
Console.Write("Элементов ? ");
n = Convert.ToInt32(Console.ReadLine());
mas = new double[n];
for (int i = 0; i < n; i++)
{
Console.Write("Mas[" + i + "]=");
mas[i] = Convert.ToDouble(Console.ReadLine());
}
}
double sum()
{ // функция суммирования
double s=0;
for(int i=0;i<mas.Length;i++)
if(mas[i]>lim)s+=mas[i];
return s;
}
public double sum_prop
{ // свойство, значение которой вычисляется через функцию
get { if (sum() > 0) return sum(); else return -25; }
}
public double lim_prop
{ // свойство, представляющее поле данных
get { return lim; }
set { if (value > 0)lim = value; else lim = 0; }
}
}
class Program
{
static void Main(string[] args)
{
Prop1 pr2=new Prop1();
// обращение к свойству для присвоения значения
pr2.lim_prop = 23.5;
// обращения к свойствам для получения значения
Console.WriteLine("Сумма " + pr2.sum_prop);
Console.WriteLine("Граница " + pr2.lim_prop);
Console.ReadLine();
} } }
Возникает вопрос: в каких случаях целесообразно использовать свойства, в чем их преимущество по сравнению с прямым обращением к полям данных или к функциям? При присвоении значений полям через свойства можно выполнять проверку корректности новых значений. К свойствам, значения которых должны вычисляться, обращаться легче по сравнению с обращением к функциям (не надо заботиться об аргументах). Как видно даже из этого простейшего примера, алгоритм вычисления значения свойства может быть записан как в самом свойстве, так и в функции, к которой обращается свойство. Кроме того, методика объектно-ориентированного программирования не рекомендует прямые обращения к полям. Те поля, к которым необходимо прямое обращение и которые, таким образом, входят в интерфейс класса, можно представить свойствами.
На использование свойств налагаются довольно серьезные ограничения. Свойству не соответствует поле памяти, оно лишь представляет данные. Поэтому его нельзя передавать методу в качестве ref- или out-параметра. Свойство не должно изменять состояние базовой переменной при вызове get.
Помните, при работе с массивами мы использовали записи mas.Length и mas.GetLength(0). Теперь мы знаем, что первая из них является свойством класса «Массив», а вторая – функцией из этого же класса.
-
3.6. Использование класса в качестве типа данных
Классы можно использовать в качестве типов данных как в других классах, так и в функциях. Рассмотрим пример использования одного класса в качестве типа данных в другом классе.
namespace Nasl22
{
class mas1
{ // класс mas1 будет в дальнейшем использован в качестве типа данных
protected int[] a;
public mas1()
{ // конструктор
int n;
Console.Write("Elements ");
n = Convert.ToInt32(Console.ReadLine());
a = new int[n];
}
public void inpt()
{ // ввод массива
for (int i = 0; i < a.Length; i++)
{
Console.Write("a[" + i + "]=");
a[i] = Convert.ToInt32(Console.ReadLine());
}
}
int sum()
{ // нахождение суммы
int s=0;
for(int i=0;i<a.Length;i++)
s+=a[i];
return s;
}
public int summa
{ // свойство
get { return sum(); }
}
public int this[int k]
{ // индексатор
get { return a[k];}
set { a[k] = value; }
}
}
class cl_a
{
public mas1 arr1; //объявим переменную типа класс mas1
int sm;
public cl_a()
{ // конструктор класса cl_a, он создает и экземпляр mas1
arr1 = new mas1();
arr1.inpt();
}
public int st1()
{ // обращение к свойству класса mas1
sm=arr1.summa;
return sm;
}
}
class Program
{
static void Main(string[] args)
{
cl_a my = new cl_a();
int n, m,r;
n = my.arr1.summa; //обращение к свойству
r = my.st1(); /* обращение к собственной функции,
которая, в свою очередь, обращается к свойству класса mas1 */
m = my.arr1[2]; // работает индексатор
Console.WriteLine("Сумма=" + n + " Сумма="+
r+" элемент [2] =" + m);
Console.ReadLine();
} } }
Следующий пример иллюстрирует использование класса в качестве типа данных при работе с функциями: введем пары «имя – шифр» и выведем имя, соответствующее максимальному значению шифра (предположим, что оно единственное). Наличие обеих строк: инициализация массива и инициализация каждого элемента массива в цикле обязательно!
namespace FunKlass
{
class dan
{ // этот класс будем использовать в качестве типа данных
public string s1;
public int k;
}
class Program
{
static dan[] fun2()
{ // функция определения количества элементов dan в массиве ,
// инициализация и ввод массива.
int n,m;
dan []w;
Console.Write("Элементов ? ");
n = Convert.ToInt32(Console.ReadLine());
w = new dan[n]; // инициализируем массив
for (int i = 0; i < n; i++)
{
w[i] = new dan(); // инициализируем элемент массива
Console.Write("Элемент " + i + " Имя ");
w[i].s1 = Console.ReadLine();
Console.Write("Элемент " + i + " Номер ");
w[i].k=Convert.ToInt32(Console.ReadLine());
}
return w;
}
static dan fun1(dan[] x)
{
string t1;
int max=0;
dan d1=new dan();
for (int i = 0; i < x.Length; i++)
{
if (max < x[i].k)
{
max = x[i].k;
d1 = x[i];
}
}
return d1;
}
static void Main(string[] args)
{
dan []b;
dan otv;
b = fun2(); //ввод массива классов
otv = fun1(b); // обработка массива классов
Console.WriteLine("Number= " + otv.k + " Name= " + otv.s1);
Console.ReadLine();
} } }
Можно использовать и следующие реализации класса dan и функции fun2 :
class dan
{
public string s1;
public int k;
public dan()
{
Console.Write("Element String ");
s1 = Console.ReadLine();
Console.Write("Element number ");
k = Convert.ToInt32(Console.ReadLine());
}
}
static dan[] fun2()
{
int n,m;
dan []w;
Console.Write("Elements ? ");
n = Convert.ToInt32(Console.ReadLine());
w = new dan[n];
for (int i = 0; i < n; i++)
{ // каждый элемент вводится конструктором
w[i] = new dan();
}
return w;
}
3.7. Работа со структурами
Класс является ссылочным типом: доступ к его объектам осуществляется с помощью ссылок. Доступ к объектам класса с помощью ссылок вызывает дополнительные накладные расходы при каждом доступе. При работе с маленькими объектами дополнительные расходы могут иметь существенное значение. С целью решения этой проблемы в C# введены структуры. Структура подобна классу, но она имеет тип «значение», но не «ссылка». Внешне объявление структуры похоже на объявление класса. Структуры могут иметь в своем составе данные, методы, индексаторы, свойства. Конструкторы тоже разрешены, но они обязательно должны иметь параметры; деструкторы – нет. Для создания экземпляра структуры можно вызвать конструктор через new, но можно и не вызывать. В таком случае экземпляр структуры создается, но записанные в конструкторе операции не будут выполнены. Структуры не могут участвовать в процессе наследования ни в качестве предков, ни в качестве потомков. Исключение: в качестве предка структуры можно указать интерфейс (об интерфейсах поговорим позже).
namespace StructFun
{
struct dan1
{
public string s1; // атрибут public обязателен
public int k;
}
class Program
{
static dan1[] inpt()
{ // ввод массива структур
dan1[] temp;
int n;
Console.Write("Elements ? ");
n = Convert.ToInt32(Console.ReadLine());
temp = new dan1[n];
for (int i = 0; i < n; i++)
{
Console.Write("Elem " + i + " Num ");
temp[i].k = Convert.ToInt32(Console.ReadLine());
Console.Write("Elem " + i + " Name ");
temp[i].s1 = Console.ReadLine();
}
return temp;
}
static double proc1(dan1 []x)
{ // обработка массива структур
int s = 0;
for (int i = 0; i < x.Length; i++)
s += x[i].k;
return (double)s / x.Length;
}
static void Main(string[] args)
{
dan1[] id; // массив исходных данных
double aver;
id = inpt(); // ввод исходных данных
aver = proc1(id); // обработка массива структур
Console.WriteLine("Average=" + aver);
Console.ReadLine();
} } }
3.8. Наследование
В C# допускается простое наследование: каждый класс может иметь только одного предка. Используя наследование, можно создать базовый класс, который определяет характеристики, присущие множеству связанных объектов. Этот класс затем может быть унаследован другими классами с добавлением в каждый из них своих особенностей. Равнозначные термины: базовый класс – класс-наследник; родительский класс – дочерний класс; класс предок – класс-наследник.
Создадим в качестве примера базовый класс для обработки массива, включающий определение массива, его ввод и вывод. К элементам базового класса с атрибутом доступа private нет доступа из классов-наследников, они, таким образом, не наследуются. Поэтому рекомендуют (если нет на этот счет особых соображений) дать элементам базового класса атрибут доступа protected.
class arr
{
protected int[] k; /* атрибут доступа protected
необходим для обеспечения доступа из классов-наследников */
public arr()
{ // конструктор 1
int n;
Console.Write("Элементов ? ");
n = Convert.ToInt32(Console.ReadLine());
k = new int[n];
for (int i = 0; i < n; i++)
{
Console.Write("K[" + i + "]=");
k[i] = Convert.ToInt32(Console.ReadLine());
}
}
public arr(int p)
{ // конструктор 2
k = new int[p];
for (int i = 0; i < p; i++)
{
Console.Write("K[" + i + "]=");
k[i] = Convert.ToInt32(Console.ReadLine());
}
}
public void output()
{
Console.WriteLine();