Создание класса двумерной матрицы c#
Нужно создать класс двумерной матрицы. При запуске выдает ошибку. В отладчике видно, что размер матрицы он видит но при этом самой матрицы как будто бы нет
using System;
namespace lab9var4
{
// Создаем класс матрица
class Matrix
{
// Создаем поля класса матрица. Т.к. нужно продемонстрировать наследование, модификатор доступа - protected
protected int n { get; set; }
protected int m { get; set; }
protected int[,] a { get; set; }
// Создаем конструктор класса матрица с параметрами: строка , столбец
public Matrix(int n, int m, int[,]a)
{
//Указываем кл. словом this параметрам на поля класса
this.n = n;
this.m = m;
// Инициализируем параметры - строку и столбец для матрицы a
// Выделяем память для матрицы указывая this`ом параметрам на поля класса n - строка/ m - столбец
a = new int[n, m];
this.a[this.n, this.m] = a[n,m];
}
// Создаем ввод размера матрицы с клавиатуры
public Matrix()
{
Console.WriteLine("Введите размер строки");
n = int.Parse(Console.ReadLine());
Console.WriteLine("Введите размер столбца");
m = int.Parse(Console.ReadLine());
}
// Создаем метод для заполнения матрицы случайными числами от 0 до 9, что бы числа не были громоздкими
public void rand()
{
Random rnd = new Random();
for (int i = 0; i < n; i++)
{
for (int w = 0; w < m; i++)
{
a[i, w] = rnd.Next(0,9);
}
}
}
// Создаем метод для печати матрицы в консоль
public void print()
{
for (int i = 0; i < n; i++)
{
Console.WriteLine();
for (int w = 0; w < m; i++)
{
Console.Write(a[i, w]+"\t");
}
}
}
// Создаем метод для нахождения суммы элементов матрицы
public int sum()
{
int sum = 0;
for (int i = 0; i < n; i++)
{
for (int w = 0; w < m; i++)
{
sum += a[i, w];
}
}
return sum;
}
// Создаем метод для нахождения максимального элемента матрицы
public int maxItem()
{
int maxItem = a[0,0];
for (int i = 0; i < n; i++)
{
for (int w = 0; w < m; i++)
{
if (maxItem < a[i,w])
{
maxItem = a[i, w];
}
}
}
return maxItem;
}
}
Создаем дочерний класс квадратной матрицы sqMatrix. P.S. sq это square - квадрат.
class sqMatrix:Matrix
{
// Создаем поля класса производного от Matrix
public int n { get; set; }
public int[,] a { get; set; }
// Создаем конструктор в котором будет только 1 параметр - размер кв. матрицы
public sqMatrix(int n)
{
this.n = n;
a = new int[this.n, this.n];
}
// Создаем ввод размера матрицы с клавиатуры
public sqMatrix()
{
Console.WriteLine("Введите размер квадратной матрицы");
n = int.Parse(Console.ReadLine());
}
// Создаем метод по нахождению суммы диагональных элементов
public int diagItemSum()
{
// count - элемент по счету, sum - сумма элементов
int count = 0,sum = 0 ;
for (int i = 0; i < n; i++)
{
for (int w = 0; w < n; i++)
{
// Элемент будет на главной диагонали при i == w
// На побочной, если номер элемента будет равен сумме его индексов
// Т.к. мы считаем с нуля то если i+w=count, a[i,w] - элемент побочной диагонали
if (((i+=w)==count)||(i==w))
{
sum += a[i, w];
}
}
count++;
}
return sum;
}
// Создаем метод по проверки матрицы - является ли она симметричной
// Матрица является симметричной если элементы по обоим сторонам гл. диагонали равны друг другу
public bool symmMatrix()
{
// В count мы подсчитываем кол-во равных элементов
int count = 0;
for (int i = 0; i < n; i++)
{
for (int w = 0; w < n; w++)
{
// Если элемент не находится на главной диагонали i!=w и элемент равен себе же при
// [i, w] = > [w, i] изменении строки на столбец, count увеличивается
if ((i != w) && a[i, w] == a[w,i])
{
count++;
}
}
}
// Теперь, если матрица симметрична: (ее размер * ее размер) - (количество элементов на гл. диагонали)
// это все остальные элементы матрицы которые мы сравниваем с count и выводим в зависимости от этого результат
if (count == ((n * n) - n))
{
return true;
}
else
{
return false;
}
}
}
Класс Program:
class Program
{
static void Main()
{
// Создаем обьект матрицы
Matrix a = new Matrix();
a.rand();
a.print();
Console.WriteLine("Сумма элементов " + a.sum());
Console.WriteLine("Максимальный элемент " + a.maxItem());
sqMatrix b = new sqMatrix();
b.print();
Console.WriteLine("Сумма диагональных элементов " + b.diagItemSum());
Console.WriteLine("Является ли матрица симметричной " + b.symmMatrix());
}
}
Ответы (2 шт):
Не инициализирован массив. В конструкторе без параметров инициализации массива нет. Только ввод размерности
// Создаем ввод размера матрицы с клавиатуры
public Matrix()
{
Console.WriteLine("Введите размер строки");
n = int.Parse(Console.ReadLine());
Console.WriteLine("Введите размер столбца");
m = int.Parse(Console.ReadLine());
a = new int[n, m]; // добавить эту строку
}
И ещё, думаю, тут опечатка
void Rand()
{
...
for (int i = 0; i < n; i++)
for (int w = 0; w < m; w++) // было i++
...
}
И это, совет: не m, n, i, w, а rowCount, columnCount, rowIndex, columnIndex. Не a, а array
@AnatolyBelanov уже дал абсолютно верный ответ, но я хочу "нырнуть глубже" и поговорить в целом о написаном коде.
Обо всём и по делу
Для начала, хочу поговорить о логических и не только ошибках, найденных, проходясь глазами по коду.
Фрагмент #1
У класса Matrix есть такой конструкор (комментарии опущены):
public Matrix(int n, int m, int[,] a)
{
this.n = n;
this.m = m;
a = new int[n, m];
this.a[this.n, this.m] = a[n, m];
}
Для чего существует параметр int[,] a? Этот аргумент является излишним и ненужным. Помимо прочего, в строке this.a[this.n, this.m] = a[n, m]; вы присваеваете элемент с индексами [n, m] массива a элементу с индексами [n, m] массива this.a. Вы же, скорее всего, хотели присвоить массив. Код следует переписать так:
public Matrix(int n, int m)
{
this.n = n;
this.m = m;
this.a = new int[n, m];
}
Если же по задумке это что-то, по-типу, конструктора копирования, то излишнимим являются n и m. В таком случае, код приобретает следующий вид:
public Matrix(int[,] a)
{
this.a = a;
this.n = a.GetLength(0);
this.m = a.GetLength(1);
}
Фрагмент #2
В классе Matrix есть следующий метод:
// Создаем метод для заполнения матрицы случайными числами от 0 до 9, что бы числа не были громоздкими
public void rand()
{
Random rnd = new Random();
for (int i = 0; i < n; i++)
{
for (int w = 0; w < m; i++)
{
a[i, w] = rnd.Next(0, 9);
}
}
}
Тут можно отметить, как минимум, 2 важных момента (остальные позволю себе опустить):
- Как указал @AnatolyBelanov, у вас опечатка в строке
for (int w = 0; w < m; i++), тут должно бытьw++. Причём эта опечатка "из уст в уста" перешла из методаrand()во все остальные. - В комментарии вы указали, что заполняете матрицу
случайными числами от 0 до 9, однако это не так: методNextклассаRandomне включает верхнюю границу, в итоге максимальное число - 8.
Фрагмент #3
В классе sqMatrix есть следующий метод:
// Создаем метод по нахождению суммы диагональных элементов
public int diagItemSum()
{
// count - элемент по счету, sum - сумма элементов
int count = 0, sum = 0;
for (int i = 0; i < n; i++)
{
for (int w = 0; w < n; i++)
{
// Элемент будет на главной диагонали при i == w
// На побочной, если номер элемента будет равен сумме его индексов
// Т.к. мы считаем с нуля то если i+w=count, a[i,w] - элемент побочной диагонали
if (((i += w) == count) || (i == w))
{
sum += a[i, w];
}
}
count++;
}
return sum;
}
Про w++ уже было сказанно, так что это пропустим.
Первое, что меня смущает в этом методе, - это строчка с уловием: ((i += w) == count) || (i == w). Почему там i += w? Или это опечатка, или какой-то сложный алгоритм.
Второе - переменная count, которая нужна для... чего? Возможно, для отладки? Но для этого лучше подойдёт отладчик.
Ну и помимо того, этот код можно упростить, да ещё и оптимизировав:
public int diagItemSum()
{
int sum = 0;
for (int i = 0, j = 0; i < n; i++, j++)
sum += a[i, j] + a[i, n - j - 1]; // суммируем элемент главной и побочной диагоналей
if (n % 2 == 1) // если размер непарный
sum -= a[n / 2, n / 2]; // то мы должны вычесть центральный элемент, потому что мы просуммировали его дважды
return sum;
}
Распределение ответственности
Существует такое понятие (один из принципов SOLID), как распределение ответсвенности. В двух словах, это значит то, что каждый объект/метод должен делать только то, для чего он предназначен; делать только одно логическое действие. Под этим подразумевается, к примеру, то, что метод рассчёта не должен выводить ничего в консоль, ведь его задача лишь выполнить рассчёт. Этот принцип помогает также повысить возможность переиспользования написанного кода.
Относительно ваше кода этот принцип следует применить для класса Matrix (и аналогично для класса sqMatrix). В этом классе существует такой конструктор (да ещё и по-умолчанию):
public Matrix()
{
Console.WriteLine("Введите размер строки");
n = int.Parse(Console.ReadLine());
Console.WriteLine("Введите размер столбца");
m = int.Parse(Console.ReadLine());
}
Тут не важно, правильно ли работает этот метод (как указал AnatolyBelanov, тут нехватает a = new int[n ,m]). Суть в следующем: конструктор - это такая "штука", которая предназначена для инициализации объекта. В идеальном случае, единственное, что должен делать контруктор - инициализировать поля и свойства переданными аргументами. Допустимо и более сложное поведение, но конструктор нужен для инициализации. Вывод же и считывание данных с консоли это то, что никак не относится к конструктору и должно быть вынесено в отдельный метод (лучше всего вне класса Matrix).
Наследование
Из написанного кода, я сделал вывод, что вы не до конца понимаете механизм наследования, по-этому, хочу немного прояснить.
Основное
Начнём с того, что наследуемый класс наследует все нестатические не приватные методы, поля и свойства. Допусти у нас есть классы ClassA и ClassB:
public class ClassA
{
public int Property1 { get; }
}
public class ClassB : ClassA
{
public void ExampleMethod()
{
Property1++; // так как ClassB унаследован от ClassA, то свойство Property1 также унаследовано, и мы в любом месте можем к нему обращаться
}
}
Перекрытие
Когда же вы, в унаследованном классе, пишите метод/поле/свойство, которые уже существуют в базовом, то вы их перекрываете. Все детали не так просто объянить, но, вкратце, вы "забываете" про унаследованный метод/поле/свойство и работаете с новосозданным. Это значит, что все "операции" базового класса, которые используют это метод/поле/свойство не будут знать о новом перекрывающем значении.
Пример:
public class ClassA
{
public int Property1 { get; set; } = 5;
public void PrintProperty1() => Console.WriteLine(Property1);
}
public class ClassB : ClassA
{
public int Property1 { get; set; } = 8; // перекрывает базовый Property1
// public void PrintProperty1() - унаследован
}
Использование и результат:
var b = new ClassB();
b.PrintProperty(); // будет выведено "5"
Конструкторы
Констуктор унаследованного класса обязан вызвать один из конструктор базового. Если у базового класса есть конструктор "по-умолчанию" (то есть, без аргументов), то неявно будет вызван он:
public class ClassA
{
public ClassA() => Console.WriteLine("Конструктор А");
}
public class ClassB : ClassA
{
// неявно вызывает base()
public ClassB() => Console.WriteLine("Конструктор B");
}
Использование и результат:
var b = new ClassB();
// Вывод:
// Конструктор А
// Конструктор B
Это причина, по которой, у вас конструкторы класса sqMatrix вызывают конструктор Matrix().
Какой конструктор вызывать можно указать явно, что применимо в вашем коде:
class sqMatrix : Matrix
{
// вызываем базовый конструктор с нужными параметрами
public sqMatrix(int n) : base(n, n, new int[n, n]) { }
// в идеале, после рефакторинга, должно быть так:
// public SquareMatrix(int size) : base(size, size) { }
}
Стиль кода
У языка C# существует конвенция именования, которая говорит о том: что, как и когда должно именоваться.
Самый простой и яркий пример правил именования - это правило именование всех классов и методов с Большой буквы.
Matrix.rand() -> Matrix.Rand()
sqMatrix -> SqMatrix
...
Второе несложное правило (возможно, оно даже является внегласным) - называть свойства, методы, классы и т.д. в соответсвии с их смыслом:
n -> Height или RowsCount
m -> Widht или ColumnsCount
a -> Matrix или BaseMatrix
...
Также, не стоит применять сокращения, которые не являются общепринятыми и/или, когда они не имеют смысла:
sqMatrix -> SquareMatrix
rnd -> random
Есть ещё много различных правил, с которыми вы можете ознакомится на MSDN, как будет время.
Комментарии
Комментарии это полезная вещь, но как и во всём, желательно придерживаться золотой середины: добавлять только там, где это и вправду необходимо. Излишние комментарии только мешают.
Нет, особо, никакого смысла в комментариях, по типу:
// Создаем класс матрица
class Matrix { ... }
// Указываем кл. словом this параметрам на поля класса
this.n = n;
Кроме того, если правильно именовать переменные и методы, то многие вещи и не нужно объяснять в комментариях. К примеру, если бы n называлось Height или RowsCount, то было бы очевидно, что переменная обозначет "высоту"/количество строк матрицы.
Также, хорошим тоном комментирования методов и классов, считается исользование <summary>. Такие комментарии могут отображаться просто при наведении курсора на соответсвующий метод, иметь подсветку, ссылки и прочее.

