класс, структура, объект, интерфейс, наследование

advertisement
Процесс структурирования кода (классы, объекты, структуры)
На примере решения квадратного уравнения рассмотрим процесс создания приложения и
реструктуризации кода.
Версия 0
Создадим решение solQuadrEq и в нем нулевую версию консольного приложения QuadrEq0 со
следующим содержанием
// Так определяются величины, регулирующие условную компиляцию
// #define означает "определи", #undef означает "отмени определение"
// При использовании, например, версии version0_0 следует поставить // перед #undef version0_0
// В этом случае будут компилироваться те операторы программы, которые окружены
// декларациями типа #if version0_0 #endif.
#define version0_0 // Это модификация версии 0, в которой коэффициенты уравнения вводятся с
консоли
//#undef version0_0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace nsQuadrEq0
{
/// <summary>
/// Определяет версию 0 решения квадратного уравнения x^2 + a[1]*x + a[0] = 0.
/// В этой версии весь код находится внутри единственного метода класса main
/// </summary>
class QuadrEq0Class
{
/// <summary>
/// Точка входа в приложение.
/// Вводит коэффициенты квадратного уравнения, решает, тестирует и выводит результат
/// </summary>
/// <param name="args">
/// Аргументы командной строки
/// </param>
static void Main(string[] args)
{
#region Объявления и инициализации
// Объявление массива a, состоящего из переменных типа double
double[] a;
// К этому моменту в переменной a содержится "ссылка в никуда" (значение null).
// Инициализация ссылки на массив a, состоящего из двух переменных типа double
a = new double[2];
// Теперь ссылка на массив a получила конкретное значение
// и элементы массива a[0],a[1] можно использовать.
#if !version0_0
// Объявление объекта rnd класса Random; rnd - ссылка на объект
1
// сразу в объявлении происходит инициализация ссылки на этот объект оператором new
// и вызовом конструктора Random() для инициализации полей объекта rnd
Random rnd = new Random();
#endif
#endregion
// Организация цикла с постусловием (типа do ... while)
do
{
Console.WriteLine("Задаем коэффициенты квадратного уравнения");
#region Задание коэффициентов уравнения
#if version0_0
// Ввод коэффициентов уравнения с консоли (версия 0_0)
Console.Write("Введите свободный член уравнения a[0]: ");
a[0] = double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед первой степенью a[1]: ");
a[1] = Double.Parse(Console.ReadLine());
#else
// Инцииализация коэффициентов случайными числами в интервале [0;1)
// (метод NextDouble() объекта rnd)
a[0] = rnd.NextDouble();
a[1] = rnd.NextDouble();
#endif
#endregion
// Комментарий к задаче
Console.WriteLine("Решаем квадратное уравнение x^2 " + (a[1] > 0 ? "+" : "-")
+ " {0}*x " + (a[0] > 0 ? "+" : "-") + " {1} = 0",
Math.Abs(a[1]),Math.Abs(a[0]));
#region Решение квадратного уравнения
// Объявление и вычисление дискриминанта квадратного уравнения discr
double discr = a[1] * a[1] - 4 * a[0];
// Объявление и определение знака дискриминанта.
// Указание на знак в форме значения true (если знак не отрицательный)
// или false (при отрицательном знаке)
// сохраняется в логической переменной discrSign
bool discrSign = discr >= 0;
// Вычисление корня из модуля дискриминанта // величины, которая используется в формулах определения корней уравнения
discr = Math.Sqrt(Math.Abs(discr));
// Объявление и инцииализация ссылок на массивы для харанения
// вещественных, мнимых частей корней и левых частей уравнения
double[]
xRe = new double[2],
xIm = new double[2],
zeroRe = new double[2],
zeroIm = new double[2];
// Ветвление, разделяющее вид корней - вещественные или комплексные
if (discrSign)
{
// Вещественные корни
xRe[0] = -.5 * (a[1] + discr);
xRe[1] = -.5 * (a[1] - discr);
xIm[0] = xIm[1] = 0;
2
for (int i = 0; i < 2; i++)
{
zeroRe[i] = xRe[i] * xRe[i] + a[1] * xRe[i] + a[0];
zeroIm[i] = 0;
}
}
else
{
// Комплексные корни
xRe[0] = xRe[1] = -.5 * a[1];
xIm[0] = -(xIm[1] = .5 * discr);
for (int i = 0; i < 2; i++)
{
zeroRe[i] = xRe[i] * xRe[i] - xIm[i] * xIm[i] + a[1] * xRe[i] + a[0];
zeroIm[i] = 2 * xRe[i] * xIm[i] + a[1] * xIm[i];
}
}
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
for (int i = 0; i < 2; i++)
{
Console.WriteLine("{0} - й корень: {1} " + (i == 0 ? "-" : "+") + " {2}i",
i + 1, xRe[i], Math.Abs(xIm[i]));
Console.WriteLine("Левая часть уравнения для {0} - ого корня: {1} " +
(zeroIm[i] >= 0 ? "+" : "-") + " {2}i", i + 1, zeroRe[i], Math.Abs(zeroIm[i]));
}
#endregion
Console.WriteLine("Press any key to restart or esc to exit");
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
}
}
Версия 1
Добавим к решению новый проект с именем QuadrEq1. В новой версии выделим методы,
управляющие инициализацией коэффициентов уравнения, самим решением и выводом
результатов. Это может выглядеть следующим образом
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
3
using System.IO;
using System.Data;
using System.Xml;
namespace nsQuadrEq1
{
/// <summary>
/// Определяет версию 1 решения квадратного уравнения x^2 + _a[1]*x + _a[0] = 0.
/// В этой версии код распределен по статическим методам
/// и локальные переменные метода main заменены статическими полями класса.
/// Статические члены класса (поля, методы) могут быть вызваны только именем самого класса
/// (в данном случае именем QuadEq1Class) или внутри статических методов этого класса.
/// В последнем случае имя класса указывать не обязательно.
/// </summary>
class QuadrEq1Class
{
#region Members (члены класса)
#region Fields (поля класса)
/// <summary>
/// Хранит ссылку на объект класса _rnd, позволяющий генерировать случайные числа
/// Поле инициализируется оператором new и вызовом конструктора класса Random()
/// </summary>
static Random _rnd = new Random();
/// <summary>
/// Хранит ссылку на массив коэффициентов уравнения.
/// </summary>
static double[] _a;
/// <summary>
/// Хранит ссылку на массив вещественных частей корней уравнения.
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
static double[] _xRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей корней уравнения
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
static double[] _xIm = new double[2];
/// <summary>
/// Хранит ссылку на массив вещественных частей левой части уравнения
/// после подстановки туда соответствующих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
static double[] _zeroRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей левой части уравнения
/// после подстановки туда соответствующих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
4
/// </summary>
static double[] _zeroIm = new double[2];
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
/// </summary>
static void ReadCoeff()
{
// Ввод коэффициентов уравнения с консоли
Console.Write("Введите свободный член уравнения: ");
_a[0] = double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед первой степенью: ");
_a[1] = Double.Parse(Console.ReadLine());
}
/// <summary>
/// Задает коэффициенты, как случайные числа в интервале [0;1)
/// </summary>
static void SetRandomCoeff()
{
// Инициализация коэффициентов случайными числами в интервале [0;1) .
// (метод NextDouble() объекта _rnd)
_a[0] = _rnd.NextDouble();
_a[1] = _rnd.NextDouble();
}
/// <summary>
/// Находит корни квадратного уравнения, и значения левой части уравнения в этих точках
/// </summary>
static void Solve()
{
// Объявление и вычисление дискриминанта квадратного уравнения discr
double discr = _a[1] * _a[1] - 4 * _a[0];
// Объявление и определение знака дискриминанта.
// Указание на знак в форме значения true (если знак не отрицательный)
// или false (при отрицательном знаке)
// сохраняется в логической переменной discrSign
bool discrSign = discr >= 0;
// Вычисление корня из модуля дискриминанта // величины, которая используется в формулах определения корней уравнения
discr = Math.Sqrt(Math.Abs(discr));
// Ветвление, разделяющее вид корней - вещественные или комплексные
if (discrSign)
{
// Вещественные корни
_xRe[0] = -.5 * (_a[1] + discr);
_xRe[1] = -.5 * (_a[1] - discr);
_xIm[0] = _xIm[1] = 0;
for (int i = 0; i < 2; i++)
{
_zeroRe[i] = _xRe[i] * _xRe[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 0;
}
5
}
else
{
// Комплексные корни
_xRe[0] = _xRe[1] = -.5 * _a[1];
_xIm[0] = -(_xIm[1] = .5 * discr);
for (int i = 0; i < 2; i++)
{
_zeroRe[i] = _xRe[i] * _xRe[i] - _xIm[i] * _xIm[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 2 * _xRe[i] * _xIm[i] + _a[1] * _xIm[i];
}
}
}
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экрану
/// </param>
static void WriteResult(TextWriter tw)
{
// Вывод результатов на носитель,
// который сопоставлен объекту tw (экран или текстовой файл)
for (int i = 0; i < 2; i++)
{
tw.WriteLine("{0} - й корень: {1} " + (i == 0 ? "-" : "+") + " {2}i",
i + 1, _xRe[i], Math.Abs(_xIm[i]));
tw.WriteLine(
"Левая часть уравнения для {0} - ого корня: {1} " + (_zeroIm[i] >= 0 ? "+" : "-") + " {2}i",
i + 1, _zeroRe[i], Math.Abs(_zeroIm[i]));
}
tw.Close();
}
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
static void WriteResult(DataSet ds)
{
// К таблице CoeffTable добавляется строка
ds.Tables[0].Rows.Add(ds.Tables[0].NewRow());
// В элементы этой строки таблицы CoeffTable заносятся значения коэффициентов
for (int i = 0; i < 2; i++)
ds.Tables[0].Rows[ds.Tables[0].Rows.Count - 1][i] = _a[i];
// К таблице XTable добавляются две строки,
// в которые помещаются соответствующие корни
for (int i = 0; i < 2; i++)
{
6
ds.Tables[1].Rows.Add(ds.Tables[1].NewRow());
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1][0] = _xRe[i];
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1][1] = _xIm[i];
}
// К таблице ZeroTable добавляются две строки,
// в которые помещаются соответствующие значения левой части уравнения
for (int i = 0; i < 2; i++)
{
ds.Tables[2].Rows.Add(ds.Tables[2].NewRow());
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1][0] = _zeroRe[i];
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1][1] = _zeroIm[i];
}
}
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
/// <param name="s">
/// Поток вывода результатов
/// </param>
static void WriteResult(Stream s)
{
if (!s.CanWrite)
{
// Если в поток нельзя писать, то метод не выполняется
Console.WriteLine(
"Поток {0} не позволяет производить в него запись данных", s.ToString());
return;
}
try // Попытка записи в поток s
{
BinaryWriter bw = new BinaryWriter(s);
for (int i = 0; i < 2; i++)
{
bw.Write(_a[i]);
bw.Write(_xRe[i]);
bw.Write(_xIm[i]);
bw.Write(_zeroRe[i]);
bw.Write(_zeroIm[i]);
}
}
catch (Exception ex)
{
// Если при записи в поток s возникла исключительная ситуация,
// управление будет передано в эту область
Console.WriteLine(ex.Message);
return;
}
}
/// <summary>
/// Точка входа в приложение
7
/// Организует ввод коэффициентов, решение и тестирование квадратного уравнения,
// и вывод результата.
/// </summary>
/// <param name="args">
/// Аргументы командной строки
/// </param>
static void Main(string[] args)
{
#region Инициализация
// Инициализация ссылки на массив коэффициентов
_a = new double[2];
#endregion
// Создание базы таблиц с данными о коэффициентах уравнения,
//корнях и значениях левых частей
using (DataSet ds = new DataSet("Quadratic Equation"))
{
// К объекту ds добавляется таблица с именем CoeffTable
ds.Tables.Add(new DataTable("CoeffTable"));
// К объекту ds добавляется таблица с именем XTable
ds.Tables.Add(new DataTable("XTable"));
// К объекту ds добавляется таблица с именем ZeroTable
ds.Tables.Add(new DataTable("ZeroTable"));
// К таблице CoeffTable добавляется колонка с именем a[0]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[0]"));
// К таблице CoeffTable добавляется колонка с именем a[1]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[1]"));
// К таблице XTable добавляется колонка с именем Re(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Re(x)"));
// К таблице XTable добавляется колонка с именем Im(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Im(x)"));
// К таблице ZeroTable добавляется колонка с именем Re(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Re(Zero)"));
// К таблице ZeroTable добавляется колонка с именем Im(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Im(Zero)"));
// Создание потока в виде бинарного файла
using (Stream s = File.Create("QuadrEq1"))
// Организация цикла с постусловием (типа do ... while)
do
{
Console.WriteLine("Задаем коэффициенты квадратного уравнения");
#region Задание коэффициентов уравнения
//ReadCoeff();
SetRandomCoeff();
#endregion
Console.WriteLine("Решаем квадратное уравнение x^2 " + (_a[1] > 0 ? "+" : "-")
+ " {0}*x " + (_a[0] > 0 ? "+" : "-") + " {1} = 0",
Math.Abs(_a[1]), Math.Abs(_a[0]));
#region Решение квадратного уравнения
Solve();
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
8
// Вывод на экран
WriteResult(Console.Out);
// Вывод в текстовой файл
WriteResult(File.AppendText("QuadrEq1_result.txt"));
// Запись в поток
WriteResult(s);
// Добавка строк в таблицы базы ds
WriteResult(ds);
#endregion
Console.WriteLine("Press any key to restart or esc to save dataset and exit");
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Данные базы ds записываются во вновь создаваемый xml-файл
ds.WriteXml("QuadrEq1.xml");
}
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
#endregion
#endregion
}
}
9
Версия 2
Новый проект QuadrEq2 содержит версию 2, в которой поля и методы описаны как нестатические.
Это требует создания экземпляра объекта класса внутри метода main.
Так выглядит новая версия кода
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Xml;
namespace nsQuadrEq2
{
/// <summary>
/// Определяет версию 2 решения квадратного уравнения x^2 + _a[1]*x + _a[0] = 0.
/// В отличие от версии 1 этой версии код распределен по обычным (не статическим) методам
/// Не статическими являются также поля класса QuadrEq2Class.
/// Для использования не статических полей и методов внутри статического метода main
/// создается экземпляр (объект) класса QuadrEq2Class.
/// Не статические члены класса относятся только
/// к экземпляру (объекту) класса, но не самому классу.
/// </summary>
class QuadrEq2Class
{
#region Members (члены класса)
#region Fields (поля класса)
/// <summary>
/// Хранит ссылку на объект класса _rnd, позволяющий генерировать случайные числа
/// Поле инициализируется оператором new и вызовом конструктора класса Random()
/// </summary>
Random _rnd = new Random();
/// <summary>
/// Хранит ссылку на массив коэффициентов уравнения.
/// </summary>
double[] _a;
/// <summary>
/// Хранит ссылку на массив вещественных частей корней уравнения.
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _xRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей корней уравнения
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _xIm = new double[2];
/// <summary>
10
/// Хранит ссылку на массив вещественных частей левой части уравнения
/// после подстановки туда соответствующих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _zeroRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей левой части уравнения
/// после подстановки туда соответствующих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _zeroIm = new double[2];
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
/// </summary>
void ReadCoeff()
{
// Ввод коэффициентов уравнения с консоли
Console.Write("Введите свободный член уравнения: ");
_a[0] = double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед первой степенью: ");
_a[1] = Double.Parse(Console.ReadLine());
}
/// <summary>
/// Задает коэффициенты как случайные числа в интервале [0;1)
/// </summary>
void SetRandomCoeff()
{
// Инициализация коэффициентов случайными числами в интервале [0;1)
// (метод NextDouble() объекта _rnd)
_a[0] = _rnd.NextDouble();
_a[1] = _rnd.NextDouble();
}
/// <summary>
/// Находит корни квадратного уравнения, и значения левой части уравнения в этих точках
/// </summary>
void Solve()
{
// Объявление и вычисление дискриминанта квадратного уравнения discr
double discr = _a[1] * _a[1] - 4 * _a[0];
// Объявление и определение знака дискриминанта.
// Указание на знак в форме значения true (если знак не отрицательный)
// или false (при отрицательном знаке)
// сохраняется в логической переменной discrSign
bool discrSign = discr >= 0;
// Вычисление корня из модуля дискриминанта // величины, которая используется в формулах определения корней уравнения
discr = Math.Sqrt(Math.Abs(discr));
// Ветвление, разделяющее вид корней - вещественные или комплексные
11
if (discrSign)
{
// Вещественные корни
_xRe[0] = -.5 * (_a[1] + discr);
_xRe[1] = -.5 * (_a[1] - discr);
_xIm[0] = _xIm[1] = 0;
for (int i = 0; i < 2; i++)
{
_zeroRe[i] = _xRe[i] * _xRe[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 0;
}
}
else
{
// Комплексные корни
_xRe[0] = _xRe[1] = -.5 * _a[1];
_xIm[0] = -(_xIm[1] = .5 * discr);
for (int i = 0; i < 2; i++)
{
_zeroRe[i] = _xRe[i] * _xRe[i] - _xIm[i] * _xIm[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 2 * _xRe[i] * _xIm[i] + _a[1] * _xIm[i];
}
}
}
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экрану
/// </param>
void WriteResult(TextWriter tw)
{
// Вывод результатов на носитель, который сопоставлен объекту tw
// (экран или текстовой файл)
for (int i = 0; i < 2; i++)
{
tw.WriteLine("{0} - й корень: {1} " + (i == 0 ? "-" : "+") + " {2}i", i + 1, _xRe[i],
Math.Abs(_xIm[i]));
tw.WriteLine(
"Левая часть уравнения для {0} - ого корня: {1} " + (_zeroIm[i] >= 0 ? "+" : "-") + " {2}i",
i + 1, _zeroRe[i], Math.Abs(_zeroIm[i]));
}
tw.Close();
}
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
void WriteResult(DataSet ds)
{
12
// К таблице CoeffTable добавляется строка
ds.Tables[0].Rows.Add(ds.Tables[0].NewRow());
// В элементы этой строки таблицы CoeffTable заносятся значения коэффициентов
for (int i = 0; i < 2; i++)
ds.Tables[0].Rows[ds.Tables[0].Rows.Count - 1][i] = _a[i];
// К таблице XTable добавляются две строки,
// в которые помещаются соответствующие корни
for (int i = 0; i < 2; i++)
{
ds.Tables[1].Rows.Add(ds.Tables[1].NewRow());
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1][0] = _xRe[i];
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1][1] = _xIm[i];
}
// К таблице ZeroTable добавляются две строки,
// в которые помещаются соответствующие значения левой части уравнения
for (int i = 0; i < 2; i++)
{
ds.Tables[2].Rows.Add(ds.Tables[2].NewRow());
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1][0] = _zeroRe[i];
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1][1] = _zeroIm[i];
}
}
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
/// <param name="s">
/// Поток вывода результатов
/// </param>
void WriteResult(Stream s)
{
if (!s.CanWrite)
{
// Если в поток нельзя писать, то метод не выполняется
Console.WriteLine("Поток {0} не позволяет производить в него запись данных", s.ToString());
return;
}
try // Попытка записи в поток s
{
BinaryWriter bw = new BinaryWriter(s);
for (int i = 0; i < 2; i++)
{
bw.Write(_a[i]);
bw.Write(_xRe[i]);
bw.Write(_xIm[i]);
bw.Write(_zeroRe[i]);
bw.Write(_zeroIm[i]);
}
}
13
catch (Exception ex)
{
// Если при записи в поток s возникла исключительная ситуация,
// управление будет передано в эту область
Console.WriteLine(ex.Message);
return;
}
}
/// <summary>
/// Точка входа в приложение
/// Организует создание экземпляра класса QuadrEq2Class, ввод коэффициентов,
/// решение и тестирование квадратного уравнения, и вывод результата.
/// </summary>
/// <param name="args">
/// Аргументы командной строки
/// </param>
static void Main(string[] args)
{
#region Инициализация
// Объявление и инициализация объекта класса QuadrEq2Class
QuadrEq2Class qe = new QuadrEq2Class();
// Везде далее обращение к полям и методам класса QuadrEq2Class
// должно содержать ссылку на объект qe!
// Инициализация ссылки на массив коэффициентов
qe._a = new double[2];
#endregion
// Создание базы таблиц с данными о коэффициентах уравнения,
// корнях и значениях левых частей
using (DataSet ds = new DataSet("Quadratic Equation"))
{
// К объекту ds добавляется таблица с именем CoeffTable
ds.Tables.Add(new DataTable("CoeffTable"));
// К объекту ds добавляется таблица с именем XTable
ds.Tables.Add(new DataTable("XTable"));
// К объекту ds добавляется таблица с именем ZeroTable
ds.Tables.Add(new DataTable("ZeroTable"));
// К таблице CoeffTable добавляется колонка с именем a[0]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[0]"));
// К таблице CoeffTable добавляется колонка с именем a[1]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[1]"));
// К таблице XTable добавляется колонка с именем Re(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Re(x)"));
// К таблице XTable добавляется колонка с именем Im(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Im(x)"));
// К таблице ZeroTable добавляется колонка с именем Re(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Re(Zero)"));
// К таблице ZeroTable добавляется колонка с именем Im(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Im(Zero)"));
// Создание потока в виде бинарного файла
using (Stream s = File.Create("QuadrEq1"))
// Организация цикла с постусловием (типа do ... while)
do
14
{
Console.WriteLine("Задаем коэффициенты квадратного уравнения");
#region Задание коэффициентов уравнения
//ReadCoeff();
qe.SetRandomCoeff();
#endregion
Console.WriteLine("Решаем квадратное уравнение x^2 " + (qe._a[1] > 0 ? "+" : "-")
+ " {0}*x " + (qe._a[0] > 0 ? "+" : "-") + " {1} = 0",
Math.Abs(qe._a[1]), Math.Abs(qe._a[0]));
#region Решение квадратного уравнения
qe.Solve();
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
// Вывод на экран
qe.WriteResult(Console.Out);
// Вывод в текстовой файл
qe.WriteResult(File.AppendText("QuadrEq1_result.txt"));
// Запись в поток
qe.WriteResult(s);
// Добавка строк в таблицы базы ds
qe.WriteResult(ds);
#endregion
Console.WriteLine("Press any key to restart or esc to save dataset and exit");
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Данные базы ds записываются во вновь создаваемый xml-файл
ds.WriteXml("QuadrEq1.xml");
}
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
#endregion
#endregion
}
}
15
Версия 3
В новой версии 3 класс решения квадратного уравнения отделен от класса, содержащего метод
main, хотя и описан в том же пространстве имен и в том же физическом файле кода. Это
отделение, однако, приводит к необходимости изменить права доступа к членам класса. В
следующем коде
 изменены права доступа к методам
 введено свойство a для доступа к полю _a
 введен конструктор, в котором инициализируется поле _a
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Xml;
namespace nsQuadrEq3
{
/// <summary>
/// Организует решение квадратного уравнения.
/// В отличие от предыдущей версии 2 во вновь созданном классе QuadrEq
/// необходимо изменить права доступа к членам класса,
/// используемым в методе main. В данном случае это относится к полю,
/// содержащему ссылку на масcив коэффициентов _a, и всем методам класса
/// </summary>
class QuadrEq
{
#region Members (члены класса)
#region Ctr
/// <summary>
/// Конструктор инициализирует поля объекта
/// </summary>
public QuadrEq()
{
// Инициализация ссылки на массив коэффициентов
_a = new double[2];
}
#endregion
// Поля обычно имеют доступ private (доступны только внутри класса).
// При отсутствии модификатора доступа у членов класса - доступ private
#region Fields (поля класса)
/// <summary>
/// Хранит ссылку на объект класса _rnd, позволяющий генерировать случайные числа
/// Поле инициализируется оператором new и вызовом конструктора класса Random()
/// </summary>
Random _rnd = new Random();
/// <summary>
16
/// Хранит ссылку на массив коэффициентов уравнения.
/// </summary>
double[] _a;
/// <summary>
/// Хранит ссылку на массив вещественных частей корней уравнения.
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _xRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей корней уравнения
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _xIm = new double[2];
/// <summary>
/// Хранит ссылку на массив вещественных частей левой части уравнения
/// после подстановки туда соответтсвуюих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _zeroRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей левой части уравнения
/// после подстановки туда соответтсвуюих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _zeroIm = new double[2];
#endregion
// Свойства создаются для доступа к полям.
#region Properties (свойства)
/// <summary>
/// Возвращает ссылку на массив коэффициентов
/// </summary>
public double[] a
{
get { return _a; }
}
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
/// </summary>
public void ReadCoeff()
{
// Ввод коэффициентов уравнения с консоли
Console.Write("Введите свободный член уравнения: ");
_a[0] = double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед первой степенью: ");
_a[1] = Double.Parse(Console.ReadLine());
}
17
/// <summary>
/// Задает коэффициенты как случайные числа в интервале [0;1)
/// </summary>
public void SetRandomCoeff()
{
// Инициализация коэффициентов случайными числами в интервале [0;1)
// (метод NextDouble() объекта _rnd)
_a[0] = _rnd.NextDouble();
_a[1] = _rnd.NextDouble();
}
/// <summary>
/// Находит корни квадратного уравнения
/// и значения левой части уравнения в этих точках
/// </summary>
public void Solve()
{
// Объявление и вычисление дискриминанта квадратного уравнения discr
double discr = _a[1] * _a[1] - 4 * _a[0];
// Объявление и определение знака дискриминанта.
// Указание на знак в форме значения true (если знак не отрицательный)
// или false (при отрицательном знаке)
// сохраняется в логической переменной discrSign
bool discrSign = discr >= 0;
// Вычисление корня из модуля дискриминанта // величины, которая используется в формулах определения корней уравнения
discr = Math.Sqrt(Math.Abs(discr));
// Ветвление, разделяющее вид корней - вещественные или комплексные
if (discrSign)
{
// Вещественные корни
_xRe[0] = -.5 * (_a[1] + discr);
_xRe[1] = -.5 * (_a[1] - discr);
_xIm[0] = _xIm[1] = 0;
for (int i = 0; i < 2; i++)
{
_zeroRe[i] = _xRe[i] * _xRe[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 0;
}
}
else
{
// Комплексные корни
_xRe[0] = _xRe[1] = -.5 * _a[1];
_xIm[0] = -(_xIm[1] = .5 * discr);
for (int i = 0; i < 2; i++)
{
_zeroRe[i] =
_xRe[i] * _xRe[i] - _xIm[i] * _xIm[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 2 * _xRe[i] * _xIm[i] + _a[1] * _xIm[i];
}
}
}
18
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экране
/// </param>
public void WriteResult(TextWriter tw)
{
// Вывод результатов на носитель, который сопоставлен объекту tw
// (экран или текстовой файл)
for (int i = 0; i < 2; i++)
{
tw.WriteLine("{0} - й корень: {1} " + (i == 0 ? "-" : "+") + " {2}i",
i + 1, _xRe[i], Math.Abs(_xIm[i]));
tw.WriteLine("Левая часть уравнения для {0} - ого корня: {1} " +
(_zeroIm[i] >= 0 ? "+" : "-") + " {2}i",
i + 1, _zeroRe[i], Math.Abs(_zeroIm[i]));
}
tw.Close();
}
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
public void WriteResult(DataSet ds)
{
// К таблице CoeffTable добавляется строка
ds.Tables[0].Rows.Add(ds.Tables[0].NewRow());
// В элементы этой строки таблицы CoeffTable заносятся значения коэффициентов
for (int i = 0; i < 2; i++)
ds.Tables[0].Rows[ds.Tables[0].Rows.Count - 1][i] = _a[i];
// К таблице XTable добавляются две строки,
// в которые помещаются соответствующие корни
for (int i = 0; i < 2; i++)
{
ds.Tables[1].Rows.Add(ds.Tables[1].NewRow());
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1][0] = _xRe[i];
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1][1] = _xIm[i];
}
// К таблице ZeroTable добавляются две строки,
// в которые помещаются соответствующие значения левой части уравнения
for (int i = 0; i < 2; i++)
{
ds.Tables[2].Rows.Add(ds.Tables[2].NewRow());
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1][0] = _zeroRe[i];
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1][1] = _zeroIm[i];
}
19
}
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
/// <param name="s">
/// Поток вывода результатов
/// </param>
public void WriteResult(Stream s)
{
if (!s.CanWrite)
{
// Если в поток нельзя писать, то метод не выполняется
Console.WriteLine(
"Поток {0} не позволяет производить в него запись данных",
s.ToString());
return;
}
try // Попытка записи в поток s
{
BinaryWriter bw = new BinaryWriter(s);
for (int i = 0; i < 2; i++)
{
bw.Write(_a[i]);
bw.Write(_xRe[i]);
bw.Write(_xIm[i]);
bw.Write(_zeroRe[i]);
bw.Write(_zeroIm[i]);
}
}
catch (Exception ex)
{
// Если при записи в поток s возникла исключительная ситуация,
// управление будет передано в эту область
Console.WriteLine(ex.Message);
return;
}
}
#endregion
#endregion
}
/// <summary>
/// В версии 3 класс, содержащий метод main отделен от класса,
/// решающего квадратное уравнение.
/// </summary>
class QuadrEq3Class
{
/// <summary>
/// Точка входа в приложение
/// Организует создание экземпляра класса QuadrEq, ввод коэффициентов,
/// решение и тестирование квадратного уравнения и вывод результата.
/// </summary>
20
/// <param name="args">
/// Аргументы командной строки
/// </param>
static void Main(string[] args)
{
#region Инициализация
// Объявление и инициализация объекта класса QuadrEq
QuadrEq qe = new QuadrEq();
// Везде далее обращение к полям и методам класса QuadrEq
// должно содержать ссылку на объект qe!
#endregion
// Создание базы таблиц с данными о коэффициентах уравнения,
// корнях и значениях левых частей
using (DataSet ds = new DataSet("Quadratic Equation"))
{
// К объекту ds добавляется таблица с именем CoeffTable
ds.Tables.Add(new DataTable("CoeffTable"));
// К объекту ds добавляется таблица с именем XTable
ds.Tables.Add(new DataTable("XTable"));
// К объекту ds добавляется таблица с именем ZeroTable
ds.Tables.Add(new DataTable("ZeroTable"));
// К таблице CoeffTable добавляется колонка с именем a[0]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[0]"));
// К таблице CoeffTable добавляется колонка с именем a[1]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[1]"));
// К таблице XTable добавляется колонка с именем Re(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Re(x)"));
// К таблице XTable добавляется колонка с именем Im(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Im(x)"));
// К таблице ZeroTable добавляется колонка с именем Re(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Re(Zero)"));
// К таблице ZeroTable добавляется колонка с именем Im(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Im(Zero)"));
// Создание потока в виде бинарного файла
using (Stream s = File.Create("QuadrEq3"))
// Организация цикла с постусловием (типа do ... while)
do
{
Console.WriteLine("Задаем коэффициенты квадратного уравнения");
#region Задание коэффициентов уравнения
//ReadCoeff();
qe.SetRandomCoeff();
#endregion
Console.WriteLine("Решаем квадратное уравнение x^2 " + (qe.a[1] > 0 ? "+" : "-")
+ " {0}*x " + (qe.a[0] > 0 ? "+" : "-") + " {1} = 0",
Math.Abs(qe.a[1]), Math.Abs(qe.a[0]));
#region Решение квадратного уравнения
qe.Solve();
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
// Вывод на экран
21
qe.WriteResult(Console.Out);
// Вывод в текстовой файл
qe.WriteResult(File.AppendText("QuadrEq3_result.txt"));
// Запись в поток
qe.WriteResult(s);
// Добавка строк в таблицы базы ds
qe.WriteResult(ds);
#endregion
Console.WriteLine("Press any key to restart or esc to save dataset and exit");
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Данные базы ds записываются во вновь создаваемый xml-файл
ds.WriteXml("QuadrEq3.xml");
}
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
}
}
22
Версия 4
В версии 4 структурирования процесса решения квадратного уравнения класс QuadEq помещается
в отдельное приложение, компилируемое в исполняющий файл библиотеки классов (с
расширением .dll).
С этой целью к проекту добавляется шаблон Class Library и в него помещается описание класса
QuadEq.
Добавляется также консольное приложение с именем QuadEq4, которое, уже не содержит класс
QuadEq, а лишь ссылку ( в узле References) на библиотеку QuadEq и дополнительную декларацию
using nsQuadEq, ссылающуюся на пространство имен этой библиотеки.
Следует отметить, что класс QuadEq в своем объявлении не имел модификатор доступа. По
умолчанию отсутствие модификатора доступа в объявлении класса означает, что доступ к классу
ограничен приложением (сборкой), в котором класс описан. Модификатор этого ограничения
доступа internal можно не указывать. Библиотека, в которую помещен класс в этой версии,
является отдельным приложением (сборкой). Поэтому уровень доступа internal оказывается
недостаточным для доступа к классу из другого приложения. Поэтому следует к заголовку класса
QuadEq добавить модификатор public.
В новой версии имеем, т.о., два приложения – библиотеку (Class Library) QuadEq с описанием
класса и приложение QuadEq4 с функцией main, использующей класс QuadEq.
Библиотека и класс QuadEq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Xml;
namespace nsQuadrEq
{
/// <summary>
/// Организует решение квадратного уравнения.
/// В отличие от предыдущей версии 3 класс QuadrEq
/// помещен в отдельный проект QuadrEq - динамически загружаемая библиотека.
/// Для обеспечения доступа класс должен быть описан с модификатором public
/// (по умолчанию модификатор доступа класса internal - доступен только внутри проекта).
/// </summary>
public class QuadrEq
{
#region Members (члены класса)
#region Ctr
/// <summary>
/// Конструктор инициализирует поля объекта
/// </summary>
public QuadrEq()
{
// Инициализация ссылки на массив коэффициентов
_a = new double[2];
}
#endregion
23
// Поля обычно имеют доступ private (доступны только внутри класса).
// При отсутствии модификатора доступа у членов класса - доступ private
#region Fields (поля класса)
/// <summary>
/// Хранит ссылку на объект класса _rnd, позволяющий генерировать случайные числа
/// Поле инициализируется оператором new и вызовом конструктора класса Random()
/// </summary>
Random _rnd = new Random();
/// <summary>
/// Хранит ссылку на массив коэффициентов уравнения.
/// </summary>
double[] _a;
/// <summary>
/// Хранит ссылку на массив вещественных частей корней уравнения.
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _xRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей корней уравнения
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _xIm = new double[2];
/// <summary>
/// Хранит ссылку на массив вещественных частей левой части уравнения
/// после подстановки туда соответтсвуюих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _zeroRe = new double[2];
/// <summary>
/// Хранит ссылку на массив мнимых частей левой части уравнения
/// после подстановки туда соответтсвуюих корней
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
double[] _zeroIm = new double[2];
#endregion
// Свойства создаются для доступа к полям.
#region Properties (свойства)
/// <summary>
/// Возвращает ссылку на массив коэффициентов
/// </summary>
public double[] a
{
get { return _a; }
}
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
24
/// </summary>
public void ReadCoeff()
{
// Ввод коэффициентов уравнения с консоли
Console.Write("Введите свободный член уравнения: ");
_a[0] = double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед первой степенью: ");
_a[1] = Double.Parse(Console.ReadLine());
}
/// <summary>
/// Задает коэффициенты как случайные числа в интервале [0;1)
/// </summary>
public void SetRandomCoeff()
{
// Инициализация коэффициентов случайными числами в интервале [0;1)
// (метод NextDouble() объекта _rnd)
_a[0] = _rnd.NextDouble();
_a[1] = _rnd.NextDouble();
}
/// <summary>
/// Находит корни квадратного уравнения
/// и значения левой части уравнения в этих точках
/// </summary>
public void Solve()
{
// Объявление и вычисление дискриминанта квадратного уравнения discr
double discr = _a[1] * _a[1] - 4 * _a[0];
// Объявление и определение знака дискриминанта.
// Указание на знак в форме значения true (если знак не отрицательный)
// или false (при отрицательном знаке)
// сохраняется в логической переменной discrSign
bool discrSign = discr >= 0;
// Вычисление корня из модуля дискриминанта // величины, которая используется в формулах определения корней уравнения
discr = Math.Sqrt(Math.Abs(discr));
// Ветвление, разделяющее вид корней - вещественные или комплексные
if (discrSign)
{
// Вещественные корни
_xRe[0] = -.5 * (_a[1] + discr);
_xRe[1] = -.5 * (_a[1] - discr);
_xIm[0] = _xIm[1] = 0;
for (int i = 0; i < 2; i++)
{
_zeroRe[i] = _xRe[i] * _xRe[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 0;
}
}
else
{
// Комплексные корни
_xRe[0] = _xRe[1] = -.5 * _a[1];
25
_xIm[0] = -(_xIm[1] = .5 * discr);
for (int i = 0; i < 2; i++)
{
_zeroRe[i] =
_xRe[i] * _xRe[i] - _xIm[i] * _xIm[i] + _a[1] * _xRe[i] + _a[0];
_zeroIm[i] = 2 * _xRe[i] * _xIm[i] + _a[1] * _xIm[i];
}
}
}
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экране
/// </param>
public void WriteResult(TextWriter tw)
{
// Вывод результатов на носитель, который сопоставлен объекту tw
// (экран или текстовой файл)
for (int i = 0; i < 2; i++)
{
tw.WriteLine("{0} - й корень: {1} " + (i == 0 ? "-" : "+") + " {2}i",
i + 1, _xRe[i], Math.Abs(_xIm[i]));
tw.WriteLine("Левая часть уравнения для {0} - ого корня: {1} " +
(_zeroIm[i] >= 0 ? "+" : "-") + " {2}i",
i + 1, _zeroRe[i], Math.Abs(_zeroIm[i]));
}
tw.Close();
}
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
public void WriteResult(DataSet ds)
{
// К таблице CoeffTable добавляется строка
ds.Tables["CoeffTable"].Rows.Add(ds.Tables["CoeffTable"].NewRow());
// В элементы этой строки таблицы CoeffTable заносятся значения коэффициентов
for (int i = 0; i < 2; i++)
ds.Tables["CoeffTable"].Rows[ds.Tables["CoeffTable"].Rows.Count-1][String.Format("a[{0}]", i)]
= _a[i];
// К таблице XTable добавляются две строки,
// в которые помещаются соответствующие корни
for (int i = 0; i < 2; i++)
{
ds.Tables[1].Rows.Add(ds.Tables[1].NewRow());
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1]["Re(x)"] = _xRe[i];
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1]["Im(x)"] = _xIm[i];
26
}
// К таблице ZeroTable добавляются две строки,
// в которые помещаются соответствующие значения левой части уравнения
for (int i = 0; i < 2; i++)
{
ds.Tables[2].Rows.Add(ds.Tables[2].NewRow());
ds.Tables[2].Rows[ds.Tables[2].Rows.Count-1]["Re(Zero)"] = _zeroRe[i];
ds.Tables[2].Rows[ds.Tables[2].Rows.Count-1]["Im(Zero)"] = _zeroIm[i];
}
}
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
/// <param name="s">
/// Поток вывода результатов
/// </param>
public void WriteResult(Stream s)
{
if (!s.CanWrite)
{
// Если в поток нельзя писать, то метод не выполняется
Console.WriteLine(
"Поток {0} не позволяет производить в него запись данных",
s.ToString());
return;
}
try // Попытка записи в поток s
{
BinaryWriter bw = new BinaryWriter(s);
for (int i = 0; i < 2; i++)
{
bw.Write(_a[i]);
bw.Write(_xRe[i]);
bw.Write(_xIm[i]);
bw.Write(_zeroRe[i]);
bw.Write(_zeroIm[i]);
}
}
catch (Exception ex)
{
// Если при записи в поток s возникла исключительная ситуация,
// управление будет передано в эту область
Console.WriteLine(ex.Message);
return;
}
}
#endregion
#endregion
}
}
27
Консольное приложение QuadEq4
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsQuadrEq;
using System.Data;
using System.IO;
namespace nsQuadrEq4
{
/// <summary>
/// В версии 4 структуризации задачи о решении квадратного уравнения
/// класс решения уравнения помещается в отдельный проект QuadrEq, компилируемый в dll.
/// В раздел references настоящего проекта добавляется ссылка на библиотеку QuadrEq
/// В using добавляется ссылка на пространство имен библиотеки nsQuadrEq
/// </summary>
class QuadrEq4Class
{
/// <summary>
/// Точка входа в приложение
/// Организует создание экземпляра класса QuadrEq, ввод коэффициентов,
/// решение и тестирование квадратного уравнения и вывод результата.
/// </summary>
/// <param name="args">
/// Аргументы командной строки
/// </param>
static void Main(string[] args)
{
#region Инициализация
// Объявление и инициализация объекта класса QuadrEq
QuadrEq qe = new QuadrEq();
// Везде далее обращение к полям и методам класса QuadrEq
// должно содержать ссылку на объект qe!
#endregion
// Создание базы таблиц с данными о коэффициентах уравнения,
// корнях и значениях левых частей
using (DataSet ds = new DataSet("Quadratic Equation"))
{
// К объекту ds добавляется таблица с именем CoeffTable
ds.Tables.Add(new DataTable("CoeffTable"));
// К объекту ds добавляется таблица с именем XTable
ds.Tables.Add(new DataTable("XTable"));
// К объекту ds добавляется таблица с именем ZeroTable
ds.Tables.Add(new DataTable("ZeroTable"));
// К таблице CoeffTable добавляется колонка с именем a[0]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[0]"));
// К таблице CoeffTable добавляется колонка с именем a[1]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[1]"));
28
// К таблице XTable добавляется колонка с именем Re(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Re(x)"));
// К таблице XTable добавляется колонка с именем Im(x)
ds.Tables["XTable"].Columns.Add(new DataColumn("Im(x)"));
// К таблице ZeroTable добавляется колонка с именем Re(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Re(Zero)"));
// К таблице ZeroTable добавляется колонка с именем Im(Zero)
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Im(Zero)"));
// Создание потока в виде бинарного файла
using (Stream s = File.Create("QuadrEq4"))
// Организация цикла с постусловием (типа do ... while)
do
{
Console.WriteLine("Задаем коэффициенты квадратного уравнения");
#region Задание коэффициентов уравнения
//qe.ReadCoeff();
qe.SetRandomCoeff();
#endregion
Console.WriteLine("Решаем квадратное уравнение x^2 " + (qe.a[1] > 0 ? "+" : "-")
+ " {0}*x " + (qe.a[0] > 0 ? "+" : "-") + " {1} = 0",
Math.Abs(qe.a[1]), Math.Abs(qe.a[0]));
#region Решение квадратного уравнения
qe.Solve();
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
// Вывод на экран
qe.WriteResult(Console.Out);
// Вывод в текстовой файл
qe.WriteResult(File.AppendText("QuadrEq4_result.txt"));
// Запись в поток
qe.WriteResult(s);
// Добавка строк в таблицы базы ds
qe.WriteResult(ds);
#endregion
Console.WriteLine("Press any key to restart or esc to save dataset and exit");
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Данные базы ds записываются во вновь создаваемый xml-файл
ds.WriteXml("QuadrEq4.xml");
}
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
}
29
}
Версия 5
В этой версии добавляется библиотека классов (Class Library), содержащая описание структуры
комплексных чисел Complex.
В дальнейшем структура Complex будет использована в новой версии класса решения квадратного
уравнения.
Самостоятельно написать консольное приложение, тестирующее структуру Complex, приведенную
ниже.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace nsComplex
{
/// <summary>
/// Представляет комплексное число как совокупность
/// вещественной и мнимой частей в виде чисел с двойной точностью типа double
/// </summary>
public struct Complex
{
#region Fields
/// <summary>
/// Хранит мнимую единицу
/// </summary>
public static readonly Complex i = new Complex(0, 1);
/// <summary>
/// Хранит комплексный нуль
/// </summary>
public static readonly Complex Zero;
/// <summary>
/// Хранит текущее значение вещественной части
/// </summary>
private double _re;
/// <summary>
/// Хранит текущее значение мнимой части
/// </summary>
double _im;
#endregion
#region Конструкторы
/// <summary>
/// Инициализирует поля структуры /// вещественную и мнимую часть комплексного числа
/// </summary>
/// <param name="re">
/// Вещественная часть
/// </param>
/// <param name="im">
/// Мнимая часть
30
/// </param>
public Complex(double re, double im)
{
_re = re;
_im = im;
}
/// <summary>
/// Создает комплексное число с заданным модулем и аргументом
/// </summary>
/// <param name="mod">
/// Модуль
/// </param>
/// <param name="arg">
/// Аргумент
/// </param>
/// <returns>
/// Комплексное число
/// </returns>
/// <remarks>
/// Вычисляет вещественную и мнимую части часть и вызывает конструктор.
/// </remarks>
static public Complex Create(double mod, double arg)
{
if (mod < 0)
throw new ArgumentOutOfRangeException(
"Модуль комплексного числа не может быть отрицательным!");
if (mod == 0)
return Complex.Zero;
return new Complex(mod * Math.Cos(arg), mod * Math.Sin(arg));
}
#endregion
#region Properties
/// <summary>
/// Возвращает вещественную часть
/// </summary>
public double re
{
get { return _re; }
}
/// <summary>
/// Возвращает мнимую часть
/// </summary>
public double im
{
get { return _im; }
}
/// <summary>
/// Возвращает модуль
/// </summary>
public double mod
{
get { return Math.Sqrt(_re * _re + _im * _im); }
31
}
/// <summary>
/// Возвращает аргумент
/// </summary>
public double arg
{
get { return Math.Atan2(_im, _re); }
}
#endregion
#region Комплексная арифметика. Переопределенные операторы
/// <summary>
/// Складывает два комплексных числа (бинарный плюс)
/// </summary>
static public Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.re + c2.re, c1.im + c2.im);
}
/// <summary>
/// Возвращает комплексное число с обратным знаком (унарный минус)
/// </summary>
static public Complex operator -(Complex c)
{
return new Complex(-c.re, -c.im);
}
/// <summary>
/// Вычитает два комплексных числа (бинарный минус)
/// </summary>
static public Complex operator -(Complex c1, Complex c2)
{
return c1 + (-c2);
}
/// <summary>
/// Умножает два комплексных числа
/// </summary>
static public Complex operator *(Complex c1, Complex c2)
{
return new Complex(c1.re * c2.re - c1.im * c2.im, c1.re * c2.im + c1.im * c2.re);
}
/// <summary>
/// Делит два комплексных числа
/// </summary>
static public Complex operator /(Complex c1, Complex c2)
{
double _mod = c2.mod;
return c1 * ~c2 / _mod / _mod;
}
/// <summary>
/// Преобразует комплексное число в сопряженное
/// </summary>
static public Complex operator ~(Complex c)
{
32
return new Complex(c.re, -c.im);
}
/// <summary>
/// Неявно преобразует действительное число в комплексное.
/// </summary>
static public implicit operator Complex(double a)
{
return new Complex(a, 0);
}
// Операторы равенства/неравенства
/// <summary>
/// Определяет равенство двух комплексных чисел
/// </summary>
/// <returns>
/// true, если числа равны, false в противном случае
/// </returns>
static public bool operator ==(Complex c1, Complex c2)
{
return c1.re == c2.re && c1.im == c2.im;
}
/// <summary>
/// Определяет неравенство двух комплексных чисел
/// </summary>
/// <returns>
/// true, если числа не равны, false в противном случае
/// </returns>
static public bool operator !=(Complex c1, Complex c2)
{
return !(c1 == c2);
}
#endregion
#region Methods
#region Переопределенные версии унаследованных виртуальных методов
/// <summary>
/// Проверяет равенство комплексных чисел
/// </summary>
/// <param name="obj">
/// Сравниваемое комплексное число
/// </param>
/// <returns>
/// true, если текущее число совпадает с аргументом, false в противном случае
/// </returns>
public override bool Equals(object obj)
{
return obj is Complex && this == (Complex)obj;
}
/// <summary>
/// Сопоставляет каждому комплексному числу хэш-код - целое число.
/// </summary>
/// <returns>
/// Хэш-код текущего числа
/// </returns>
33
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
/// <summary>
/// Формирует текстовое представление комплексного числа
/// </summary>
/// <returns>
/// Строка, отвечающая текстовому представлению
/// </returns>
public override string ToString()
{
return _re == 0 && _im == 0 ? "0" :
((_re != 0 ? _re.ToString() : "") + (_im == 0 ? "" : (_im < 0 ? "-" : _re == 0 ? "" : "+") +
(Math.Abs(_im) == 1 ? "" : Math.Abs(_im).ToString()) + "i"));
}
#endregion
#region Собственные методы
/// <summary>
/// Извлекает квадратный корень из комплексного числа, получая результат с положительной
/// вещественной частью
/// </summary>
/// <param name="c">
/// Комплексное число - аргумент
/// </param>
/// <returns>
/// Комплексное число - результат
/// </returns>
public static Complex Sqrt(Complex c)
{
return Complex.Create(Math.Sqrt(c.mod), .5 * c.arg);
}
/// <summary>
/// Формирует текстовое представление комплексного числа в заданном формате
/// </summary>
/// <param name="format">
/// Строка формата
/// </param>
/// <returns>
/// Строка, представляющая комплексное число в выбранном формате
/// </returns>
public string ToString(string format)
{
return _re == 0 && _im == 0 ? "0" :
((_re != 0 ? _re.ToString(format) : "") +
(_im == 0 ? "" : (_im < 0 ? "-" : _re == 0 ? "" : "+") +
(Math.Abs(_im) == 1 ? "" : Math.Abs(_im).ToString(format)) + "i"));
}
#endregion
#endregion
}
}
34
Версия 6
Во-первых, в структуре Complex после тестирования обнаруживается ошибка в перегрузке
оператора деления:
 Внутри оператора / имеется обращение к делению комплексного числа на число типа
double
double _mod = c2.mod;
return c1 * ~c2 / _mod / _mod;
 С другой стороны, в определении структуры Complex присутствует оператор неявного
(implicit) преобразования объекта типа double в объект типа Complex
static public implicit operator Complex(double a)
{
return new Complex(a, 0);
}
 Поэтому в операторе ~c2/_mod (из кода оператора деления) объект _mod типа double
вначале преобразуется в объект типа Complex, а затем выполняется операция деления.
 Но указанный участок кода сам находится внутри оператора деления! Обращение
происходит к этому же оператору. Такое обращение называется рекурсивным. В данном
случае это рекурсивное обращение ничем не ограничено, поэтому приводит к
бесконечной последовательности вызовов оператора деления. В конечном итоге, память,
выделяемая под стек, используемый оператором деления, оказывается исчерпанной, и
приложение порождает объект исключительной ситуации класса StackOverflowException.
 Ошибка легко исправляется новым кодом оператора деления:
double _mod = c2.mod, mod_2 = 1.0 / _mod / _mod;
return c1 * ~c2 * mod_2;
В этой версии отсутствует деление комплексного числа на вещественное, а лишь деление
вещественных чисел.
Во-вторых, если возникает необходимость в пересылке бинарного значения комплексного числа в
поток, следует написать наследника класса BinaryWriter, добавив в него метод Write с аргументом
– объектом типа Complex. Это можно сделать непосредственно внутри структуры Complex.
Еще одной важной особенностью следующей версии структуры Complex будет использование
нового наследования – наследования от интерфейса.
При обычном выводе комплексного числа C оператором Console.WriteLine("C = {0}", C)
вещественная и мнимая части комплексного числа выводятся в замещающем формате g (15
знаков) и в том виде, который диктуется описанным в структуре методом ToString() (без
параметров), перекрывающим виртуальный метод класса Object. Выполнение оператора
Console.WriteLine(C.ToString("f5")) распечатает число C в требуемом формате f5 (5 знаков после
запятой). Здесь явно используется другой метод ToString(string format), также описанный в
структуре Complex.
Но если попытаться вывести число на экран оператором Console.WriteLine("C = {0:f5}", C), указав
форматирующую строку внутри оператора WriteLine, то будет использован тотже метод ToString()
(без параметра), и число будет выведено по-прежнему в замещающем формате g (испытайте
сами!).
Существует, однако, механизм, позволяющий снабдить структуру Complex способностью
реагировать на форматирующую строку в вышеприведенном примере кода
Console.WriteLine("C = {0:f5}", C).
Этот механизм называется наследование интерфейсов.
В библиотеке .NET имеется множество классов, которые несут в себе только описания и
полностью лишены кодов реализации. Это так называемые интерфейсы. Любой класс, структура
35
могут наследовать любое количество этих интерфейсов. Смысл в том, что при наследовании класс
берет на себя обязательство реализовать объявленные интерфейсом методы. Сделав это, класс
становится типом, совместимым с унаследованным интерфейсом. Поэтому везде, где
используется тип унаследованного интерфейса, можно подставлять ссылку на объект классанаследника-реализатора.
Воспользуемся этим механизмом на примере структуры Complex. Поставим ее предком
интерфейс IFormattable и реализуем его единственный метод ToString с двумя параметрами,
переписав код структуры в виде
public struct Complex : IFormattable
{
#region Fields
/// <summary>
/// Хранит мнимую единицу
/// </summary>
public static readonly Complex i = new Complex(0, 1);
/// <summary>
/// Хранит комплексный нуль
/// </summary>
public static readonly Complex Zero;
/// <summary>
/// Хранит текущее значение вещественной части
/// </summary>
private double _re;
/// <summary>
/// Хранит текущее значение мнимой части
/// </summary>
double _im;
#endregion
#region Конструкторы
/// <summary>
/// Инициализирует поля структуры /// вещественную и мнимую часть комплексного числа
/// </summary>
/// <param name="re">
/// Вещественная часть
/// </param>
/// <param name="im">
/// Мнимая часть
/// </param>
public Complex(double re, double im)
{
_re = re;
_im = im;
}
/// <summary>
/// Создает комплексное число с заданным модулем и аргументом
/// </summary>
/// <param name="mod">
/// Модуль
/// </param>
/// <param name="arg">
/// Аргумент
36
/// </param>
/// <returns>
/// Комплексное число
/// </returns>
/// <remarks>
/// Вычисляет вещественную и мнимую части часть и вызывает конструктор.
/// </remarks>
static public Complex Create(double mod, double arg)
{
if (mod < 0)
throw new ArgumentOutOfRangeException(
"Модуль комплексного числа не может быть отрицательным!");
if (mod == 0)
return Complex.Zero;
return new Complex(mod * Math.Cos(arg), mod * Math.Sin(arg));
}
#endregion
#region Properties
/// <summary>
/// Возвращает вещественную часть
/// </summary>
public double re
{
get { return _re; }
}
/// <summary>
/// Возвращает мнимую часть
/// </summary>
public double im
{
get { return _im; }
}
/// <summary>
/// Возвращает модуль
/// </summary>
public double mod
{
get { return Math.Sqrt(_re * _re + _im * _im); }
}
/// <summary>
/// Возвращает аргумент в интервале (-pi; pi]
/// </summary>
public double arg
{
get { return Math.Atan2(_im, _re); }
}
#endregion
#region Комплексная арифметика. Переопределенные операторы
/// <summary>
/// Складывает два комплексных числа (бинарный плюс)
/// </summary>
37
static public Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.re + c2.re, c1.im + c2.im);
}
/// <summary>
/// Возвращает комплексное число с обратным знаком (унарный минус)
/// </summary>
static public Complex operator -(Complex c)
{
return new Complex(-c.re, -c.im);
}
/// <summary>
/// Вычитает два комплексных числа (бинарный минус)
/// </summary>
static public Complex operator -(Complex c1, Complex c2)
{
return c1 + (-c2);
}
/// <summary>
/// Умножает два комплексных числа
/// </summary>
static public Complex operator *(Complex c1, Complex c2)
{
return new Complex(c1.re * c2.re - c1.im * c2.im, c1.re * c2.im + c1.im * c2.re);
}
/// <summary>
/// Делит два комплексных числа
/// </summary>
static public Complex operator /(Complex c1, Complex c2)
{
double _mod = c2.mod, mod_2 = 1 / _mod / _mod;
return c1 * ~c2 * mod_2;
}
/// <summary>
/// Преобразует комплексное число в сопряженное
/// </summary>
static public Complex operator ~(Complex c)
{
return new Complex(c.re, -c.im);
}
/// <summary>
/// Неявно преобразует действительное число в комплексное.
/// </summary>
static public implicit operator Complex(double a)
{
return new Complex(a, 0);
}
// Операторы равенства/неравенства
/// <summary>
/// Определяет равенство двух комплексных чисел
/// </summary>
/// <returns>
38
/// true, если числа равны, false в противном случае
/// </returns>
static public bool operator ==(Complex c1, Complex c2)
{
return c1.re == c2.re && c1.im == c2.im;
}
/// <summary>
/// Определяет неравенство двух комплексных чисел
/// </summary>
/// <returns>
/// true, если числа не равны, false в противном случае
/// </returns>
static public bool operator !=(Complex c1, Complex c2)
{
return !(c1 == c2);
}
#endregion
#region Methods
#region Переопределенные версии унаследованных виртуальных методов
/// <summary>
/// Проверяет равенство комплексных чисел
/// </summary>
/// <param name="obj">
/// Сравниваемое комплексное число
/// </param>
/// <returns>
/// true, если текущее число совпадает с аргументом, false в противном случае
/// </returns>
public override bool Equals(object obj)
{
return obj is Complex && this == (Complex)obj;
}
/// <summary>
/// Сопоставляет каждому комплексному числу хэш-код - целое число.
/// </summary>
/// <returns>
/// Хэш-код текущего числа
/// </returns>
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
/// <summary>
/// Формирует текстовое представление комплексного числа
/// </summary>
/// <returns>
/// Строка, отвечающая текстовому представлению
/// </returns>
public override string ToString()
{
return _re == 0 && _im == 0 ? "0" :
((_re != 0 ? _re.ToString() : "") + (_im == 0 ? "" : (_im < 0 ? "-" : _re == 0 ? "" : "+") +
39
(Math.Abs(_im) == 1 ? "" : Math.Abs(_im).ToString()) + "i"));
}
#endregion
#region Собственные методы
/// <summary>
/// Формирует текстовое представление комплексного числа в заданном формате
/// </summary>
/// <param name="format">
/// Строка формата
/// </param>
/// <returns>
/// Строка, представляющая комплексное число в выбранном формате
/// </returns>
public string ToString(string format)
{
return
ToString(format, null); // Вызов реализации ToString интерфейса IFormattable
/*
_re == 0 && _im == 0 ? "0" :
((_re != 0 ? _re.ToString(format) : "") +
(_im == 0 ? "" : (_im < 0 ? "-" : _re == 0 ? "" : "+") +
(Math.Abs(_im) == 1 ? "" : Math.Abs(_im).ToString(format)) + "i"));
*/
}
#endregion
#region Реализация метода ToString интерфейса IFormattable
/// <summary>
/// Форматирует значение текущего экземпляра с использованием заданного формата
/// </summary>
/// <param name="format">
/// Объект, задающий используемый формат
/// </param>
/// <param name="provider">
/// Объект, используемый для форматирования значения
/// </param>
/// <returns>
/// Значение текущего экзмепляра в заданном формате
/// </returns>
public string ToString(string format, IFormatProvider formatProvider)
{
return _re == 0 && _im == 0 ? "0" :
((_re != 0 ? _re.ToString(format, formatProvider) : "") +
(_im == 0 ? "" : (_im < 0 ? "-" : _re == 0 ? "" : "+") +
(Math.Abs(_im) == 1 ? "" : Math.Abs(_im).ToString(format, formatProvider)) + "i"));
}
#endregion
#endregion
/// <summary>
/// Доопределяет метод Write для структуры Complex
/// </summary>
public class BinaryWriter : System.IO.BinaryWriter
{
40
/// <summary>
/// Вызывает конструктор предка, не выполняя иных действий
/// </summary>
/// <param name="s"></param>
public BinaryWriter(Stream s)
: base(s)
{
}
/// <summary>
/// Посылает объект типа Complex в бинарном виде в поток
/// </summary>
/// <param name="c">
/// Значение комплексного числа, посылаемого в поток
/// </param>
public void Write(Complex c)
{
base.Write(c.re); base.Write(c.im);
}
}
}
Проверьте работу новой версии на том же примере кода Console.WriteLine("C = {0:f5}", C).
В следующей версии необходимо изменить класс QuadrEq, используя в нем структуру Complex.
Сделайте это самостоятельно и измените приложение, использующее класс QuadrEq.
41
Версия 7
Новая версия класса QuadrEq, которую назовем QuadrEqx поместим в новую библиотеку с тем же
именем, сославшись на библиотеку с структурой Complex и написав следующий код класса
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Xml;
using nsComplex;
namespace nsQuadrEqX
{
/// <summary>
/// Организует решение квадратного уравнения.
/// В отличие от предыдущей версии класс QuadrEqX
/// использует структуру комплексных чисел, что позволяет упростить код
/// </summary>
public class QuadrEqX
{
#region Members (члены класса)
// Поля обычно имеют доступ private (доступны только внутри класса).
// При отсутствии модификатора доступа у членов класса - доступ private
#region Fields (поля класса)
/// <summary>
/// Хранит ссылку на объект класса _rnd, позволяющий генерировать случайные числа
/// Поле инициализируется оператором new и вызовом конструктора класса Random()
/// </summary>
Random _rnd = new Random();
/// <summary>
/// Хранит ссылку на массив коэффициентов уравнения.
/// </summary>
double[] _a=new double[2];
/// <summary>
/// Хранит ссылку на массив корней уравнения.
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
Complex[] _x = new Complex[2];
/// <summary>
/// Хранит ссылку на массив значений левой части уравнения
/// после подстановки туда соответствуюих корней
/// </summary>
Complex[] _zero = new Complex[2];
#endregion
// Свойства создаются для доступа к полям.
#region Properties (свойства)
/// <summary>
42
/// Возвращает ссылку на массив коэффициентов
/// </summary>
public double[] a
{
get { return _a; }
}
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
/// </summary>
public void ReadCoeff()
{
// Ввод коэффициентов уравнения с консоли
Console.Write("Введите свободный член уравнения: ");
_a[0] = double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед первой степенью: ");
_a[1] = Double.Parse(Console.ReadLine());
}
/// <summary>
/// Задает коэффициенты как случайные числа в интервале [0;1)
/// </summary>
public void SetRandomCoeff()
{
// Инициализация коэффициентов случайными числами в интервале [0;1)
// (метод NextDouble() объекта _rnd)
_a[0] = _rnd.NextDouble();
_a[1] = _rnd.NextDouble();
}
/// <summary>
/// Находит корни квадратного уравнения
/// и значения левой части уравнения в этих точках
/// </summary>
public void Solve()
{
// Вычисляется корень из дискриминанта (не зависимо от его знака, т.к. корень
комплексный)
Complex sqrt = Complex.Sqrt(_a[1] * _a[1] - 4 * _a[0]);
// Вычисляются корни
_x[0] = -.5 * (_a[1] + sqrt);
_x[1] = -.5 * (_a[1] - sqrt);
// Вычисляются значения левой части уравнения
for (int i = 0; i < 2; i++)
_zero[i] = _x[i] * _x[i] + _a[1] * _x[i] + _a[0];
}
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экране
/// </param>
public void WriteResult(TextWriter tw)
43
{
// Вывод результатов на носитель, который сопоставлен объекту tw
// (экран или текстовой файл)
for (int i = 0; i < 2; i++)
{
tw.WriteLine("{0} - й корень: {1} " , i + 1, _x[i]);
tw.WriteLine("Левая часть уравнения для {0} - ого корня:\n {1} ",
}
tw.Close();
i + 1, _zero[i]);
}
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
public void WriteResult(DataSet ds)
{
// К таблице CoeffTable добавляется строка
ds.Tables["CoeffTable"].Rows.Add(ds.Tables["CoeffTable"].NewRow());
// В элементы этой строки таблицы CoeffTable заносятся значения коэффициентов
for (int i = 0; i < 2; i++)
ds.Tables["CoeffTable"].Rows[ds.Tables["CoeffTable"].Rows.Count-1][String.Format("a[{0}]", i)]
= _a[i];
// К таблице XTable добавляются две строки,
// в которые помещаются соответствующие корни
for (int i = 0; i < 2; i++)
{
ds.Tables[1].Rows.Add(ds.Tables[1].NewRow());
ds.Tables[1].Rows[ds.Tables[1].Rows.Count-1]["X"] = _x[i];
}
// К таблице ZeroTable добавляются две строки,
// в которые помещаются соответствующие значения левой части уравнения
for (int i = 0; i < 2; i++)
{
ds.Tables[2].Rows.Add(ds.Tables[2].NewRow());
ds.Tables[2].Rows[ds.Tables[2].Rows.Count-1]["Zero"] = _zero[i];
}
}
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
/// <param name="s">
/// Поток вывода результатов
/// </param>
public void WriteResult(Stream s)
{
if (!s.CanWrite)
44
{
// Если в поток нельзя писать, то метод не выполняется
Console.WriteLine(
"Поток {0} не позволяет производить в него запись данных",
s.ToString());
return;
}
try // Попытка записи в поток s
{
Complex.BinaryWriter bw = new Complex.BinaryWriter(s);
for (int i = 0; i < 2; i++)
{
bw.Write(_a[i]);
bw.Write(_x[i]);
bw.Write(_zero[i]);
}
}
catch (Exception ex)
{
// Если при записи в поток s возникла исключительная ситуация,
// управление будет передано в эту область
Console.WriteLine(ex.Message);
return;
}
}
#endregion
#endregion
}
}
Добавьте консольное приложение QuadrEq7 с тестированием новой версии класса. При этом в
ссылки надо добавить библиотеки QuadrEqX и dllComplex.
Тестирующее приложение будет лишь немного отличаться от версии 4
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
using System.Xml;
using nsComplex;
using nsQuadrEqX;
namespace nsQuadrEq7
{
class QuadrEq7Class
{
static void Main(string[] args)
{
#region Инициализация
// Объявление и инициализация объекта класса QuadrEq
QuadrEqX qe = new QuadrEqX();
// Везде далее обращение к полям и методам класса QuadrEq
45
// должно содержать ссылку на объект qe!
#endregion
// Создание базы таблиц с данными о коэффициентах уравнения,
// корнях и значениях левых частей
using (DataSet ds = new DataSet("Quadratic Equation"))
{
// К объекту ds добавляется таблица с именем CoeffTable
ds.Tables.Add(new DataTable("CoeffTable"));
// К объекту ds добавляется таблица с именем XTable
ds.Tables.Add(new DataTable("XTable"));
// К объекту ds добавляется таблица с именем ZeroTable
ds.Tables.Add(new DataTable("ZeroTable"));
// К таблице CoeffTable добавляется колонка с именем a[0]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[0]"));
// К таблице CoeffTable добавляется колонка с именем a[1]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[1]"));
// К таблице XTable добавляется колонка с именем X
ds.Tables["XTable"].Columns.Add(new DataColumn("X"));
// К таблице ZeroTable добавляется колонка с именем Zero
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Zero"));
// Создание потока в виде бинарного файла
using (Stream s = File.Create("QuadrEq7"))
// Организация цикла с постусловием (типа do ... while)
do
{
Console.WriteLine("Задаем коэффициенты квадратного уравнения");
#region Задание коэффициентов уравнения
//qe.ReadCoeff();
qe.SetRandomCoeff();
#endregion
Console.WriteLine("Решаем квадратное уравнение x^2 " + (qe.a[1] > 0 ? "+" : "-")
+ " {0}*x " + (qe.a[0] > 0 ? "+" : "-") + " {1} = 0",
Math.Abs(qe.a[1]), Math.Abs(qe.a[0]));
#region Решение квадратного уравнения
qe.Solve();
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
// Вывод на экран
qe.WriteResult(Console.Out);
// Вывод в текстовой файл
qe.WriteResult(File.AppendText("QuadrEq7_result.txt"));
// Запись в поток
qe.WriteResult(s);
// Добавка строк в таблицы базы ds
qe.WriteResult(ds);
#endregion
Console.WriteLine("Press any key to restart or esc to save dataset and exit");
46
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Данные базы ds записываются во вновь создаваемый xml-файл
ds.WriteXml("QuadrEq7.xml");
}
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
}
}
Проверьте работу класса QuadrEqX с помощью этого приложения.
Подготовьте новый класс CubeEq, который решает кубическое уравнение x3 + a2x2 + a1x + a0 = 0 по
аналогии с квадратным уравнением, используя в методе Solve две версии формул, определяющих
корни уравнения.
Замена x = y – a2/3 приводит исходное уравнение к виду:
y3 + py + q = 0, где p = - a22/3 + a1; q = (a2/3)*[2(a2/3)2 – a1] + a0.
Дискриминант уравнения Q = (p/3)3 + (q/2)2.
1-ая версия
A = [-q/2 + Q1/2]1/3; B = [-q/2 – Q1/2]1/3.
Выбираются значения кубических корней так, чтобы произведение AB = -p/3.
Другими словами, B = -p/3/A.
Корни равны
y1 = A + B;
y2 = -(A + B)/2 + √3*(A - B)/2*i;
y3 = -(A + B)/2 - √3*(A - B)/2*i;
2-ая версия
Для вещественных корней (условие Q < 0) применяются формулы
y1 = 2(-p/3)1/2*cos(α/3)
y2 = -2(-p/3)1/2*cos(α/3 + π/3)
y3 = -2(-p/3)1/2*cos(α/3 - π/3),
где cos(α) = -q/2/(-p/3)3/2.
В случае Q >=0 – те же формулы, что в предыдущей версии.
47
Версия 8
Первая версия класса решения кубического уравнения может выглядеть следующим образом
#define AB // используется первый метод расчета корней
//#undef AB
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsComplex;
using System.IO;
using System.Data;
namespace nsCubicEq
{
public class CubicEq
{
#region Members (члены класса)
// Поля обычно имеют доступ private (доступны только внутри класса).
// При отсутствии модификатора доступа у членов класса - доступ private
#region Fields (поля класса)
/// <summary>
/// Хранит ссылку на объект класса _rnd, позволяющий генерировать случайные числа
/// Поле инициализируется оператором new и вызовом конструктора класса Random()
/// </summary>
Random _rnd = new Random();
/// <summary>
/// Хранит ссылку на массив коэффициентов уравнения.
/// </summary>
double[] _a = new double[3];
/// <summary>
/// Хранит ссылку на массив корней уравнения.
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
Complex[] _x = new Complex[3];
/// <summary>
/// Хранит ссылку на массив значений левой части уравнения
/// после подстановки туда соответствуюих корней
/// </summary>
Complex[] _zero = new Complex[3];
#endregion
// Свойства создаются для доступа к полям.
#region Properties (свойства)
/// <summary>
/// Возвращает ссылку на массив коэффициентов
/// </summary>
public double[] a
{
get { return _a; }
}
48
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
/// </summary>
public void ReadCoeff()
{
// Ввод коэффициентов уравнения с консоли
Console.Write("Введите свободный член уравнения: ");
_a[0] = double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед первой степенью: ");
_a[1] = Double.Parse(Console.ReadLine());
Console.Write("Введите коэффициент перед второй степенью: ");
_a[2] = Double.Parse(Console.ReadLine());
}
/// <summary>
/// Задает коэффициенты, как случайные числа в интервале [0;1)
/// </summary>
public void SetRandomCoeff()
{
// Инициализация коэффициентов случайными числами в интервале [0;1)
// (метод NextDouble() объекта _rnd)
_a[0] = 20 * _rnd.NextDouble() - 10;
_a[1] = 20 * _rnd.NextDouble() - 10;
_a[2] = 20 * _rnd.NextDouble() - 10;
}
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экрану
/// </param>
public void WriteResult(TextWriter tw)
{
// Вывод результатов на носитель, который сопоставлен объекту tw
// (экран или текстовой файл)
for (int i = 0; i < 3; i++)
{
tw.WriteLine("{0} - й корень: {1} ", i + 1, _x[i]);
tw.WriteLine("Левая часть уравнения для {0} - ого корня\n {1} ", i + 1, _zero[i]);
}
tw.Close();
}
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
public void WriteResult(DataSet ds)
{
// К таблице CoeffTable добавляется строка
49
ds.Tables["CoeffTable"].Rows.Add(ds.Tables["CoeffTable"].NewRow());
// В элементы этой строки таблицы CoeffTable заносятся значения коэффициентов
for (int i = 0; i < 3; i++)
ds.Tables["CoeffTable"].Rows[ds.Tables["CoeffTable"].Rows.Count - 1][String.Format("a[{0}]",
i)] = _a[i];
// К таблице XTable добавляются три строки,
// в которые помещаются соответствующие корни
for (int i = 0; i < 3; i++)
{
ds.Tables[1].Rows.Add(ds.Tables[1].NewRow());
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1]["X"] = _x[i];
}
// К таблице ZeroTable добавляются три строки,
// в которые помещаются соответствующие значения левой части уравнения
for (int i = 0; i < 3; i++)
{
ds.Tables[2].Rows.Add(ds.Tables[2].NewRow());
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1]["Zero"] = _zero[i];
}
}
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
/// <param name="s">
/// Поток вывода результатов
/// </param>
public void WriteResult(Stream s)
{
if (!s.CanWrite)
{
// Если в поток нельзя писать, то метод не выполняется
Console.WriteLine(
"Поток {0} не позволяет производить в него запись данных",
s.ToString());
return;
}
try // Попытка записи в поток s
{
Complex.BinaryWriter bw = new Complex.BinaryWriter(s);
for (int i = 0; i < 3; i++)
{
bw.Write(_a[i]);
bw.Write(_x[i]);
bw.Write(_zero[i]);
}
}
catch (Exception ex)
{
50
// Если при записи в поток s возникла исключительная ситуация,
// управление будет передано в эту область
Console.WriteLine(ex.Message);
return;
}
}
/// <summary>
/// Решает кубическое уравнение x^3 + a[2]*x^2 + a[1]*x + a[0] = 0
/// </summary>
public void Solve()
{
#if AB
double
a3 = a[2] / 3.0,
p = -a[2] * a3 + a[1],
q = a3 * (2 * a3 * a3 - a[1]) + a[0],
p3 = p / 3.0,
q2 = .5 * q,
Q = p3 * p3 * p3 + q2 * q2;
// Замена x = y - a/3 приводит исходное уравнение к виду
// y^3 + py +q = 0, где p = - a^2/3 + b; q = (a/3)*[2(a/3)^2 - b] + c.
// Характер решения зависит от знака дискриминанта Q = (p/3)^3 + (q/2)^2
Complex
sqrtQ = Complex.Sqrt(Q),
d1 = -q2 + sqrtQ,
A = Complex.Create(Math.Pow(d1.mod, 1 / 3.0), d1.arg / 3),// + (Q > 0 ? 0 : 2 * Math.PI / 3)),
B = -p / 3 / A;
// A = [-q/2 + Q^(1/2)]^(1/3); B = [-q/2 - Q^(1/2)]^(1/3)
// y1 = A + B;
// y2 = -(A + B)/2 + 3^(1/2)*(A - B)/2*i;
// y3 = -(A + B)/2 - 3^(1/2)*(A - B)/2*i;
_x[0] = A + B - a3;
_x[1] = -.5 * (A + B) + Complex.i * .5 * (A - B) * Math.Sqrt(3) - a3;
_x[2] = -.5 * (A + B) - Complex.i * .5 * (A - B) * Math.Sqrt(3) - a3;
#else
double
a3 = a[2] / 3.0,
p = -a[2] * a3 + a[1],
q = a3 * (2 * a3 * a3 - a[1]) + a[0],
p3 = p / 3.0,
q2 = .5 * q,
Q = p3 * p3 * p3 + q2 * q2;
// Замена x = y - a/3 приводит исходное уравнение к виду
// y^3 + py +q = 0, где p = - a^2/3 + b; q = (a/3)*[2(a/3)^2 - b] + c.
// Характер решения зависит от знака дискриминанта Q = (p/3)^3 + (q/2)^2
if (Q < 0)
{ // Вещественные корни
double
sqrtp3 = Math.Sqrt(-p3),
cosalpa = -q2 / sqrtp3 / sqrtp3 / sqrtp3,
alpha = Math.Acos(cosalpa);
51
// y1 = 2(-p/3)^(1/2)*cos(alpha/3);
// y2 = -2(-p/3)^(1/2)*cos(alpha/3 + Pi/3);
// y3 = -2(-p/3)^(1/2)*cos(alpha/3 - Pi/3);
_x[0] = 2 * sqrtp3 * Math.Cos(alpha / 3.0) - a3;
_x[1] = -2 * sqrtp3 * Math.Cos((alpha + Math.PI) / 3.0) - a3;
_x[2] = -2 * sqrtp3 * Math.Cos((alpha - Math.PI) / 3.0) - a3;
}
else
{ // Один корень вещественный; два комплексно сопряженные
double
sqrtQ = Math.Sqrt(Q),
d1 = -q2 + sqrtQ,
A = Math.Pow(Math.Abs(d1), 1 / 3.0) * Math.Sign(d1),
d2 = -q2 - sqrtQ,
B = Math.Pow(Math.Abs(d2), 1 / 3.0) * Math.Sign(d2);
// A = [-q/2 + Q^(1/2)]^(1/3); B = [-q/2 - Q^(1/2)]^(1/3)
// y1 = A + B;
// y2 = -(A + B)/2 + 3^(1/2)*(A - B)/2*i;
// y2 = -(A + B)/2 - 3^(1/2)*(A - B)/2*i;
_x[0] = A + B - a3;
_x[1] = new Complex(-.5 * (A + B) - a3, .5 * (A - B) * Math.Sqrt(3));
_x[2] = ~_x[1];
}
#endif
for (int i = 0; i < 3; i++)
_zero[i] = _x[i] * (_x[i] * (_x[i] + _a[2]) + _a[1]) + _a[0];
}
#endregion
#endregion
}
}
Тестирующее приложение можно написать по образу приложения, использованного для
тестирования класса решения квадратного уравнения
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsCubicEq;
using nsComplex;
using System.Data;
using System.IO;
namespace nsCubicEqTest
{
class CubicEqTestClass
{
static void Main(string[] args)
{
#region Инициализация
// Объявление и инициализация объекта класса CubeEq
CubicEq ce = new CubicEq();
52
// Везде далее обращение к полям и методам класса CubeEq
// должно содержать ссылку на объект ce!
#endregion
// Создание базы таблиц с данными о коэффициентах уравнения,
// корнях и значениях левых частей
using (DataSet ds = new DataSet("Cubic Equation"))
{
// К объекту ds добавляется таблица с именем CoeffTable
ds.Tables.Add(new DataTable("CoeffTable"));
// К объекту ds добавляется таблица с именем XTable
ds.Tables.Add(new DataTable("XTable"));
// К объекту ds добавляется таблица с именем ZeroTable
ds.Tables.Add(new DataTable("ZeroTable"));
// К таблице CoeffTable добавляется колонка с именем a[0]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[0]"));
// К таблице CoeffTable добавляется колонка с именем a[1]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[1]"));
// К таблице CoeffTable добавляется колонка с именем a[2]
ds.Tables["CoeffTable"].Columns.Add(new DataColumn("a[2]"));
// К таблице XTable добавляется колонка с именем X
ds.Tables["XTable"].Columns.Add(new DataColumn("X"));
// К таблице ZeroTable добавляется колонка с именем Zero
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Zero"));
// Создание потока в виде бинарного файла
using (Stream s = File.Create("CubicEq"))
// Организация цикла с постусловием (типа do ... while)
do
{
Console.WriteLine("Задаем коэффициенты кубического уравнения");
#region Задание коэффициентов уравнения
//ce.ReadCoeff();
ce.SetRandomCoeff();
#endregion
Console.WriteLine("Решаем кубическое уравнение\n x^3 " +
(ce.a[2] > 0 ? "+" : "-")
+ " {0}*x^2 " + (ce.a[1] > 0 ? "+" : "-") + " {1}*x " +
(ce.a[0] > 0 ? "+" : "-") + " {2} = 0",
Math.Abs(ce.a[2]), Math.Abs(ce.a[1]), Math.Abs(ce.a[0]));
#region Решение кубического уравнения
ce.Solve();
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
// Вывод на экран
ce.WriteResult(Console.Out);
// Вывод в текстовой файл
ce.WriteResult(File.AppendText("CubicEq_result.txt"));
// Запись в поток
53
ce.WriteResult(s);
// Добавка строк в таблицы базы ds
ce.WriteResult(ds);
#endregion
Console.WriteLine("Press any key to restart or esc to save dataset and exit");
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Данные базы ds записываются во вновь создаваемый xml-файл
ds.WriteXml("CubicEq.xml");
}
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
}
}
В следующей версии будет изменена структура Complex. К ней, кроме уже имеющейся
реализации интерфейса IFormattable, добавятся реализации интерфейсов IConvertible,
IComparable, IComparable<Complex>, IEquatable<Complex>.
Познакомьтесь с содержанием этих интерфейсов и подумайте над тем, как добавить их
реализации.
Версия 9
В этой версии будет изменена структура Complex так, что она будет наследником не только
интерфейса IFormattable, но и интерфейсов, указанных в следующем ниже коде.
Изучите новую версию и интерфейсы, которые она реализует. Напишите тестирующее
приложение, в котором проверьте работу новой версии структуры.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace nsComplex
{
/// <summary>
/// Представляет комплексное число как совокупность
/// вещественной и мнимой частей в виде чисел с двойной точностью типа double
/// </summary>
public struct Complex : IFormattable, IConvertible, IComparable,
IComparable<Complex>, IEquatable<Complex>
{
#region Fields
/// <summary>
/// Хранит мнимую единицу
/// </summary>
54
public static readonly Complex i = new Complex(0, 1);
/// <summary>
/// Хранит комплексный нуль
/// </summary>
public static readonly Complex Zero;
/// <summary>
/// Хранит текущее значение вещественной части
/// </summary>
private double _re;
/// <summary>
/// Хранит текущее значение мнимой части
/// </summary>
double _im;
#endregion
#region Конструкторы
/// <summary>
/// Инициализирует поля структуры /// вещественную и мнимую часть комплексного числа
/// </summary>
/// <param name="re">
/// Вещественная часть
/// </param>
/// <param name="im">
/// Мнимая часть
/// </param>
public Complex(double re, double im)
{
_re = re;
_im = im;
}
/// <summary>
/// Создает комплексное число с заданным модулем и аргументом
/// </summary>
/// <param name="mod">
/// Модуль
/// </param>
/// <param name="arg">
/// Аргумент
/// </param>
/// <returns>
/// Комплексное число
/// </returns>
/// <remarks>
/// Вычисляет вещественную и мнимую части часть и вызывает конструктор.
/// </remarks>
static public Complex Create(double mod, double arg)
{
if (mod < 0)
throw new ArgumentOutOfRangeException(
"Модуль комплексного числа не может быть отрицательным!");
if (mod == 0)
return Complex.Zero;
55
return new Complex(mod * Math.Cos(arg), mod * Math.Sin(arg));
}
#endregion
#region Properties
/// <summary>
/// Возвращает вещественную часть
/// </summary>
public double re
{
get { return _re; }
}
/// <summary>
/// Возвращает мнимую часть
/// </summary>
public double im
{
get { return _im; }
}
/// <summary>
/// Возвращает модуль
/// </summary>
public double mod
{
get { return Math.Sqrt(_re * _re + _im * _im); }
}
/// <summary>
/// Возвращает аргумент в интервале (-pi; pi]
/// </summary>
public double arg
{
get { return Math.Atan2(_im, _re); }
}
#endregion
#region Комплексная арифметика. Переопределенные операторы
/// <summary>
/// Складывает два комплексных числа (бинарный плюс)
/// </summary>
static public Complex operator +(Complex c1, Complex c2)
{
return new Complex(c1.re + c2.re, c1.im + c2.im);
}
/// <summary>
/// Возвращает комплексное число с обратным знаком (унарный минус)
/// </summary>
static public Complex operator -(Complex c)
{
return new Complex(-c.re, -c.im);
}
/// <summary>
/// Вычитает два комплексных числа (бинарный минус)
/// </summary>
56
static public Complex operator -(Complex c1, Complex c2)
{
return c1 + (-c2);
}
/// <summary>
/// Умножает два комплексных числа
/// </summary>
static public Complex operator *(Complex c1, Complex c2)
{
return new Complex(c1.re * c2.re - c1.im * c2.im, c1.re * c2.im + c1.im * c2.re);
}
/// <summary>
/// Делит два комплексных числа
/// </summary>
static public Complex operator /(Complex c1, Complex c2)
{
double _mod = c2.mod, mod_2 = 1 / _mod / _mod;
return c1 * ~c2 * mod_2;
}
/// <summary>
/// Преобразует комплексное число в сопряженное
/// </summary>
static public Complex operator ~(Complex c)
{
return new Complex(c.re, -c.im);
}
/// <summary>
/// Неявно преобразует действительное число в комплексное.
/// </summary>
static public implicit operator Complex(double a)
{
return new Complex(a, 0);
}
// Операторы равенства/неравенства
/// <summary>
/// Определяет равенство двух комплексных чисел
/// </summary>
/// <returns>
/// true, если числа равны, false в противном случае
/// </returns>
static public bool operator ==(Complex c1, Complex c2)
{
return c1.re == c2.re && c1.im == c2.im;
}
/// <summary>
/// Определяет неравенство двух комплексных чисел
/// </summary>
/// <returns>
/// true, если числа не равны, false в противном случае
/// </returns>
static public bool operator !=(Complex c1, Complex c2)
{
57
return !(c1 == c2);
}
#endregion
#region Methods
#region Переопределенные версии унаследованных виртуальных методов
/// <summary>
/// Проверяет равенство комплексных чисел
/// </summary>
/// <param name="obj">
/// Сравниваемое комплексное число
/// </param>
/// <returns>
/// true, если текущее число совпадает с аргументом, false в противном случае
/// </returns>
public override bool Equals(object obj)
{
return obj != null && obj is Complex && Equals((Complex)obj);
}
/// <summary>
/// Сопоставляет каждому комплексному числу хэш-код - целое число.
/// </summary>
/// <returns>
/// Хэш-код текущего числа
/// </returns>
public override int GetHashCode()
{
return re.GetHashCode() ^ im.GetHashCode();
}
/// <summary>
/// Формирует текстовое представление комплексного числа
/// </summary>
/// <returns>
/// Строка, отвечающая текстовому представлению
/// </returns>
public override string ToString()
{
// Вызов реализации метода ToString с параметром-строкой форматирования
return ToString(null);
}
#endregion
#region Собственные методы
/// <summary>
/// Определяет квадратный корень из комплексного числа
/// с положительной вещественной частью
/// </summary>
/// <param name="c">
/// Аргумент
/// </param>
/// <returns>
/// Квадратный корень из аргумента, имеющий положительную вещественную часть
/// </returns>
58
public static Complex Sqrt(Complex c)
{
return Complex.Create(Math.Sqrt(c.mod), .5 * c.arg);
}
/// <summary>
/// Формирует текстовое представление комплексного числа в заданном формате
/// </summary>
/// <param name="format">
/// Строка формата
/// </param>
/// <returns>
/// Строка, представляющая комплексное число в выбранном формате
/// </returns>
public string ToString(string format)
{
return
ToString(format, null); // Вызов реализации ToString интерфейса IFormattable
}
#endregion
#region Реализации методов унаследованных интерфейсов
/// <summary>
/// Форматирует значение текущего экземпляра с использованием заданного формата
/// </summary>
/// <param name="format">
/// Объект, задающий используемый формат
/// </param>
/// <param name="provider">
/// Объект, используемый для форматирования значения
/// </param>
/// <returns>
/// Значение текущего экзмепляра в заданном формате
/// </returns>
/// <remarks>
/// Реализация метода интерфейса IFormattable
/// </remarks>
public string ToString(string format, IFormatProvider formatProvider)
{
return _re == 0 && _im == 0 ? "0" :
((_re != 0 ? _re.ToString(format, formatProvider) : "") +
(_im == 0 ? "" : (_im < 0 ? "-" : _re == 0 ? "" : "+") +
(Math.Abs(_im) == 1 ? "" : Math.Abs(_im).ToString(format, formatProvider)) + "i"));
}
/// <summary>
/// Определяет равенство двух комплексных чисел
/// </summary>
/// <param name="c">
/// Аргумент, с которым сравнивается вызывающий объект
/// </param>
/// <returns>
/// true, если вызывающий обхект равен аргументу, и false в противном случае
/// </returns>
/// <remarks>
59
/// Реализация метода интерфейса IEuatable(Complex)
/// </remarks>
public bool Equals(Complex c)
{
return this == c;
}
/// <summary>
/// Сравнивает два комплексных числа, сравнивая значения их модулей
/// </summary>
/// <param name="c">
/// Аргмент. с которым сравнивается вызывающее число
/// </param>
/// <returns>
/// -1, если модуль вызывающего числа меньше модуля аргумента,
/// 0, если модули равны, 1, если модуль вызывающего числа больше модуля аргумента.
/// </returns>
public int CompareTo(Complex c)
{
// Сравниваются модули mod вызывающего объекта и аргумента
return mod.CompareTo(c.mod);
}
/// <summary>
/// Сравнивает два комплексных числа, сравнивая значения их модулей
/// </summary>
/// <param name="obj">
/// Комплексное число, с которым сравнивается экземпляр, вызывающий метод
/// </param>
/// <returns>
/// -1, если вызывающий экземпляр имеет модуль меньший модуля аргумента,
/// 0, если оба модуля равны, и 1,
/// если модуль вызывающего объекта больше модуля аргумента.
/// </returns>
/// <exception cref="ArgumentException">
/// Объект порождается, если тип аргумента obj не является Complex.
/// </exception>
public int CompareTo(object obj)
{
if (!(obj is Complex))
// Создается объект исключительной ситуации типа Argument Exception
// если аргумент не относится к типу Complex
throw new ArgumentException(
"obj is not the same type as this instance: Тип obj не совпадает с Complex!");
// Вызываем метод с параметром типа Complex
return CompareTo((Complex)obj);
}
#region IConvertible methods implementation
public TypeCode GetTypeCode()
{
return TypeCode.Object;
}
bool IConvertible.ToBoolean(IFormatProvider provider)
{
60
return this != Complex.Zero;
}
byte IConvertible.ToByte(IFormatProvider provider)
{
return ((IConvertible)mod).ToByte(provider);
}
sbyte IConvertible.ToSByte(IFormatProvider provider)
{
return ((IConvertible)mod).ToSByte(provider);
}
char IConvertible.ToChar(IFormatProvider provider)
{
return ((IConvertible)mod).ToChar(provider);
}
short IConvertible.ToInt16(IFormatProvider provider)
{
return ((IConvertible)mod).ToInt16(provider);
}
ushort IConvertible.ToUInt16(IFormatProvider provider)
{
return ((IConvertible)mod).ToUInt16(provider);
}
int IConvertible.ToInt32(IFormatProvider provider)
{
return ((IConvertible)mod).ToInt32(provider);
}
uint IConvertible.ToUInt32(IFormatProvider provider)
{
return ((IConvertible)mod).ToUInt32(provider);
}
long IConvertible.ToInt64(IFormatProvider provider)
{
return ((IConvertible)mod).ToInt64(provider);
}
ulong IConvertible.ToUInt64(IFormatProvider provider)
{
return ((IConvertible)mod).ToUInt64(provider);
}
float IConvertible.ToSingle(IFormatProvider provider)
{
return ((IConvertible)mod).ToSingle(provider);
}
double IConvertible.ToDouble(IFormatProvider provider)
{
return mod;
}
decimal IConvertible.ToDecimal(IFormatProvider provider)
{
return ((IConvertible)mod).ToDecimal(provider);
}
DateTime IConvertible.ToDateTime(IFormatProvider provider)
{
61
return ((IConvertible)mod).ToDateTime(provider);
}
string IConvertible.ToString(IFormatProvider provider)
{
return ToString(null, provider);
}
object IConvertible.ToType(Type conversionType, IFormatProvider provider)
{
return Convert.ChangeType(mod, conversionType);
}
#endregion
#endregion
#endregion
/// <summary>
/// Доопределяет метод Write для структуры Complex
/// </summary>
public class BinaryWriter : System.IO.BinaryWriter
{
/// <summary>
/// Вызывает конструктор предка, не выполняя иных действий
/// </summary>
/// <param name="s"></param>
public BinaryWriter(Stream s)
: base(s)
{
}
/// <summary>
/// Посылает объект типа Complex в бинарном виде в поток
/// </summary>
/// <param name="c">
/// Значение комплексного числа, посылаемого в поток
/// </param>
public void Write(Complex c)
{
base.Write(c.re); base.Write(c.im);
}
}
}
}
Следующим шагом является объединение кодов классов решения квадратного уравнения в
общий код класса PolynomEq, в котором будут реализованы все методы кроме Solve. Последний
будет объявлен виртуальным и реализован в наследниках – новых версия классов QuadrEq и
CubicEq.
Подумайте над этим.
62
Версия 10
Из содержания классов QuadrEq и CubicEq следует, что ряд методов этих классов очень похожи и
могут быть объединены и записаны в общем виде как методв нового класса, создаваемого для
решения уравнения произвольного порядка.
Класс PolynomEq
Назовем новый класс PolynomEq, и, добавив в решение ClassLibrary, запишем его код в виде
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsComplex;
using System.IO;
using System.Data;
using nsIPolynomEq;
namespace nsPolynomEq
{
public abstract class PolynomEq : IPolynomEq
{
#region Members (члены класса)
// Поля обычно имеют доступ private (доступны только внутри класса).
// При отсутствии модификатора доступа у членов класса - доступ private
#region Fields (поля класса)
/// <summary>
/// Хранит ссылку на объект класса _rnd, позволяющий генерировать случайные числа
/// Поле инициализируется оператором new и вызовом конструктора класса Random()
/// </summary>
Random _rnd = new Random();
/// <summary>
/// Хранит ссылку на массив коэффициентов уравнения.
/// </summary>
double[] _a;
/// <summary>
/// Хранит ссылку на массив корней уравнения.
/// Поле инициализируется оператором new
/// с созданием ссылки на массив из двух чисел типа double.
/// </summary>
Complex[] _x;
/// <summary>
/// Хранит ссылку на массив значений левой части уравнения
/// после подстановки туда соответствуюих корней
/// </summary>
Complex[] _zero;
#endregion
// Свойства создаются для доступа к полям.
#region Properties (свойства)
/// <summary>
/// Возвращает порядок полинома
63
/// </summary>
public int N
{
private set;
get;
}
/// <summary>
/// Возвращает ссылку на массив коэффициентов
/// </summary>
public double[] a
{
get { return _a; }
}
#endregion
#region Indexer
/// <summary>
/// Возвращает решение с заданным индексом
/// </summary>
/// <param name="i">
/// Значение индекса
/// </param>
/// <returns>
/// Решение или NaN, если решение не найдено
/// </returns>
public Complex this[int i]
{
get { return (_x == null ? Double.NaN : _x[i]); }
}
#endregion
#region Ctr
/// <summary>
/// Инициализирует поля экземпляра
/// </summary>
/// <param name="n">
/// Порядок полинома уравнения
/// </param>
protected PolynomEq(int n)
{
if (n <= 1)
throw new ArgumentException("Порядок полинома должен быть больше единицы!");
N = n;
_a = new double[n];
_zero = new Complex[n];
}
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
/// </summary>
public void ReadCoeff()
{
// Ввод коэффициентов уравнения с консоли
64
for (int i = 0; i < N; i++)
{
Console.Write(
(i == 0 ? "Введите свободный член уравнения:{0}" :
"Введите коэффициент перед {0}-ой степенью"), (i == 0 ? "" : i.ToString()));
_a[i] = double.Parse(Console.ReadLine());
}
}
/// <summary>
/// Задает коэффициенты случайными числами в интервале [-10;10)
/// </summary>
public void SetRandomCoeff()
{
// Инициализация коэффициентов случайными числами в интервале [-10;10)
// (метод NextDouble() объекта _rnd)
for (int i = 0; i < N; i++)
_a[i] = 20 * _rnd.NextDouble() - 10;
}
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экране
/// </param>
public void WriteResult(TextWriter tw)
{
// Вывод результатов на носитель, который сопоставлен объекту tw
// (экран или текстовой файл)
for (int i = 0; i < N; i++)
{
tw.WriteLine("{0} - й корень: {1} ", i + 1, _x[i]);
tw.WriteLine("Левая часть уравнения для {0} - ого корня\n {1} ", i + 1, _zero[i]);
}
tw.Close();
}
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
public void WriteResult(DataSet ds)
{
// К таблице CoeffTable добавляется строка
ds.Tables["CoeffTable"].Rows.Add(ds.Tables["CoeffTable"].NewRow());
// В элементы этой строки таблицы CoeffTable заносятся значения коэффициентов
for (int i = 0; i < N; i++)
ds.Tables["CoeffTable"].Rows[ds.Tables["CoeffTable"].Rows.Count - 1][String.Format("a[{0}]", i)]
= _a[i];
// К таблице XTable добавляются три строки,
65
// в которые помещаются соответствующие корни
for (int i = 0; i < N; i++)
{
ds.Tables[1].Rows.Add(ds.Tables[1].NewRow());
ds.Tables[1].Rows[ds.Tables[1].Rows.Count - 1]["X"] = _x[i];
}
// К таблице ZeroTable добавляются три строки,
// в которые помещаются соответствующие значения левой части уравнения
for (int i = 0; i < N; i++)
{
ds.Tables[2].Rows.Add(ds.Tables[2].NewRow());
ds.Tables[2].Rows[ds.Tables[2].Rows.Count - 1]["Zero"] = _zero[i];
}
}
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
/// <param name="s">
/// Поток вывода результатов
/// </param>
public void WriteResult(Stream s)
{
if (!s.CanWrite)
{
// Если в поток нельзя писать, то метод не выполняется
Console.WriteLine(
"Поток {0} не позволяет производить в него запись данных",
s.ToString());
return;
}
try // Попытка записи в поток s
{
Complex.BinaryWriter bw = new Complex.BinaryWriter(s);
for (int i = 0; i < N; i++)
{
bw.Write(_a[i]);
bw.Write(_x[i]);
bw.Write(_zero[i]);
}
}
catch (Exception ex)
{
// Если при записи в поток s возникла исключительная ситуация,
// управление будет передано в эту область
Console.WriteLine(ex.Message);
return;
}
}
/// <summary>
/// Метод поиска корней уравнения, реализуемый в наследниках
66
/// </summary>
/// <returns>
/// Ссылку на массив корней уравнения порядка N
/// </returns>
protected abstract Complex[] getRoots();
/// <summary>
/// Решает уравнение x^N + a[N-1]*x^(N-1) +...+ a[1]*x + a[0] = 0
/// и определяет значения левых частей уравнения после подстановки в них значений
найденных корней.
/// </summary>
public void Solve()
{
// Определение корней уравнения
_x = getRoots();
// Подсчет левых частей уравнения для найденных корней
for (int i = 0; i < N; i++)
{
_zero[i] = _a[0];
Complex xPow = 1;
for (int j = 1; j < N; j++)
_zero[i] += _a[j] * (xPow *= _x[i]);
_zero[i] += xPow * _x[i];
}
}
#endregion
#endregion
}
}
Интерфейс IPolynomEq
В дополнение к этому абстрактному классу напишем интерфейс IPolynomEq, который этим
классом реализуется
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsComplex;
using System.IO;
using System.Data;
namespace nsIPolynomEq
{
public interface IPolynomEq
{
// Свойства создаются для доступа к полям.
#region Properties (свойства)
/// <summary>
/// Возвращает порядок полинома
/// </summary>
int N
{
67
get;
}
/// <summary>
/// Возвращает ссылку на массив коэффициентов
/// </summary>
double[] a
{
get;
}
#endregion
#region Indexer
/// <summary>
/// Возвращает решение с заданным индексом
/// </summary>
/// <param name="i">
/// Значение индекса
/// </param>
/// <returns>
/// Решение или NaN, если решение не найдено
/// </returns>
Complex this[int i]
{
get;
}
#endregion
#region Methods (методы класса)
/// <summary>
/// Читает коэффициенты с консоли
/// </summary>
void ReadCoeff();
/// <summary>
/// Задает коэффициенты случайными числами
/// </summary>
void SetRandomCoeff();
/// <summary>
/// Пишет результат в текстовой файл или на экран
/// </summary>
/// <param name="tw">
/// Объект, отвечающий текстовому файлу или экране
/// </param>
void WriteResult(TextWriter tw);
/// <summary>
/// Заполняет таблицы базы данных
/// </summary>
/// <param name="ds">
/// База данных
/// </param>
void WriteResult(DataSet ds);
/// <summary>
/// Отдает результаты в поток
/// (сохраняет в бинарном файле или передает в сеть или в локальную память)
/// </summary>
68
/// <param name="s">
/// Поток вывода результатов
/// </param>
void WriteResult(Stream s);
/// <summary>
/// Решает уравнение x^N + a[N-1]*x^(N-1) +...+ a[1]*x + a[0] = 0
/// и определяет значения левых частей уравнения
/// после подстановки в них значений найденных корней.
/// </summary>
void Solve();
#endregion
}
}
Классы QuadraticEquation и CubicEquation
Изменим классы решения квадратного и кубического уравнений, сделав их наследниками класса
PolynomEq и, следовательно, Наследниками IPolynomEq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsComplex;
using nsPolynomEq;
namespace nsQuadraticEquation
{
public class QuadraticEquation:PolynomEq
{
/// <summary>
/// Конструктор. Инициализирует поля, вызывая конструктор базового класса.
/// </summary>
public QuadraticEquation()
: base(2)
{
}
/// <summary>
/// Вычисляет корни квадратного уравнения
/// </summary>
/// <returns>
/// Массив корней
/// </returns>
protected override Complex[] getRoots()
{
Complex[] x = new Complex[2];
// Вычисляется корень из дискриминанта
//(не зависимо от его знака, т.к. корень комплексный)
Complex sqrt = Complex.Sqrt(a[1] * a[1] - 4 * a[0]);
// Вычисляются корни
x[0] = -.5 * (a[1] + sqrt);
x[1] = -.5 * (a[1] - sqrt);
69
return x;
}
}
}
#define AB
//#undef AB
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsComplex;
using nsPolynomEq;
namespace nsCubicEquation
{
public class CubicEquation:PolynomEq
{
/// <summary>
/// Конструктор. Инициализирует поля, вызывая конструктор базового класса.
/// </summary>
public CubicEquation()
: base(3)
{
}
/// <summary>
/// Вычисляет корни кубического уравнения
/// </summary>
/// <returns>
/// Массив трех комплексных корней
/// </returns>
protected override Complex[] getRoots()
{
Complex[] x = new Complex[3];
#if AB
double
a3 = a[2] / 3.0,
p = -a[2] * a3 + a[1],
q = a3 * (2 * a3 * a3 - a[1]) + a[0],
p3 = p / 3.0,
q2 = .5 * q,
Q = p3 * p3 * p3 + q2 * q2;
// Замена x = y - a[2]/3 приводит исходное уравнение к виду
// y^3 + py +q = 0, где p = - a[2]^2/3 + a[1]; q = (a[2]/3)*[2(a[2]/3)^2 - a[1]] + a[0].
// Характер решения зависит от знака дискриминанта Q = (p/3)^3 + (q/2)^2
Complex
sqrtQ = Complex.Sqrt(Q),
d1 = -q2 + sqrtQ,
A = Complex.Create(Math.Pow(d1.mod, 1 / 3.0), d1.arg / 3),
B = -p3 / A;
// A = [-q/2 + Q^(1/2)]^(1/3); B = [-q/2 - Q^(1/2)]^(1/3)
70
// y1 = A + B;
// y2 = -(A + B)/2 + 3^(1/2)*(A - B)/2*i;
// y3 = -(A + B)/2 - 3^(1/2)*(A - B)/2*i;
x[0] = A + B - a3;
x[1] = -.5 * (A + B) + Complex.i * .5 * (A - B) * Math.Sqrt(3) - a3;
x[2] = -.5 * (A + B) - Complex.i * .5 * (A - B) * Math.Sqrt(3) - a3;
#else
double
a3 = a[2] / 3.0,
p = -a[2] * a3 + a[1],
q = a3 * (2 * a3 * a3 - a[1]) + a[0],
p3 = p / 3.0,
q2 = .5 * q,
Q = p3 * p3 * p3 + q2 * q2;
// Замена x = y - a/3 приводит исходное уравнение к виду
// y^3 + py +q = 0, где p = - a^2/3 + b; q = (a/3)*[2(a/3)^2 - b] + c.
// Характер решения зависит от знака дискриминанта Q = (p/3)^3 + (q/2)^2
if (Q < 0)
{ // Вещественные корни
double
sqrtp3 = Math.Sqrt(-p3),
cosalpa = -q2 / sqrtp3 / sqrtp3 / sqrtp3,
alpha = Math.Acos(cosalpa);
// y1 = 2(-p/3)^(1/2)*cos(alpha/3);
// y2 = -2(-p/3)^(1/2)*cos(alpha/3 + Pi/3);
// y3 = -2(-p/3)^(1/2)*cos(alpha/3 - Pi/3);
x[0] = 2 * sqrtp3 * Math.Cos(alpha / 3.0) - a3;
x[1] = -2 * sqrtp3 * Math.Cos((alpha + Math.PI) / 3.0) - a3;
x[2] = -2 * sqrtp3 * Math.Cos((alpha - Math.PI) / 3.0) - a3;
}
else
{ // Один корень вещественный; два комплексно сопряженные
double
sqrtQ = Math.Sqrt(Q),
d1 = -q2 + sqrtQ,
A = Math.Pow(Math.Abs(d1), 1 / 3.0) * Math.Sign(d1),
d2 = -q2 - sqrtQ,
B = Math.Pow(Math.Abs(d2), 1 / 3.0) * Math.Sign(d2);
// A = [-q/2 + Q^(1/2)]^(1/3); B = [-q/2 - Q^(1/2)]^(1/3)
// y1 = A + B;
// y2 = -(A + B)/2 + 3^(1/2)*(A - B)/2*i;
// y2 = -(A + B)/2 - 3^(1/2)*(A - B)/2*i;
x[0] = A + B - a3;
x[1] = new Complex(-.5 * (A + B) - a3, .5 * (A - B) * Math.Sqrt(3));
x[2] = ~_x[1];
}
#endif
return x;
}
}
}
71
Тестирующее консольное приложение
Напишем тестирующее консольное приложение
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using nsIPolynomEq;
using nsQuadraticEquation;
using nsCubicEquation;
using System.Data;
using System.IO;
namespace nsEqTest
{
class EqTest
{
static String sign(double d)
{
return d > 0 ? "+" : "-";
}
static void Main(string[] args)
{
#region Инициализация
Console.Write("Порядок полинома уравнения = ");
int n = int.Parse(Console.ReadLine());
// Объявление и инициализация объекта eq класса IPolynomEq
IPolynomEq eq = n == 2 ?
(IPolynomEq)new QuadraticEquation() : (IPolynomEq)new CubicEquation();
// Везде далее обращение к полям и методам класса IPolynomEq
// должно содержать ссылку на объект eq!
#endregion
// Создание базы таблиц с данными о коэффициентах уравнения,
// корнях и значениях левых частей
using (DataSet ds = new DataSet((n == 3 ? "Cubic" : "Quadratic") + " Equation"))
{
// К объекту ds добавляется таблица с именем CoeffTable
ds.Tables.Add(new DataTable("CoeffTable"));
// К объекту ds добавляется таблица с именем XTable
ds.Tables.Add(new DataTable("XTable"));
// К объекту ds добавляется таблица с именем ZeroTable
ds.Tables.Add(new DataTable("ZeroTable"));
// К таблице CoeffTable добавляются столбцы для коэффициентов уравнения
for (int i = 0; i < n; i++)
ds.Tables["CoeffTable"].Columns.Add(new DataColumn(String.Format("a[{0}]", i)));
// К таблице XTable добавляется колонка с именем X
ds.Tables["XTable"].Columns.Add(new DataColumn("X"));
// К таблице ZeroTable добавляется колонка с именем Zero
ds.Tables["ZeroTable"].Columns.Add(new DataColumn("Zero"));
// Создание потока в виде бинарного файла
72
using (Stream s = File.Create("Eq"))
// Организация цикла с постусловием (типа do ... while)
do
{
Console.WriteLine("Задаем коэффициенты " +
(n == 3 ? "кубического" : "квадратного") + " уравнения");
#region Задание коэффициентов уравнения
//eq.ReadCoeff();
eq.SetRandomCoeff();
#endregion
if (n == 3)
Console.WriteLine("Решаем кубическое уравнение\n x^3 " +
sign(eq.a[2]) + " {0}*x^2 " + sign(eq.a[1]) + " {1}*x " +
sign(eq.a[0]) + " {2} = 0",
Math.Abs(eq.a[2]), Math.Abs(eq.a[1]), Math.Abs(eq.a[0]));
else
Console.WriteLine("Решаем квадратное уравнение x^2 " + sign(eq.a[1])
+ " {0}*x " + sign(eq.a[0]) + " {1} = 0",
Math.Abs(eq.a[1]), Math.Abs(eq.a[0]));
#region Решение уравнения
eq.Solve();
#endregion
Console.WriteLine("Выводим результаты решения и тестирования");
#region Вывод результатов решения и тестирования
// Вывод на экран
eq.WriteResult(Console.Out);
// Вывод в текстовой файл
eq.WriteResult(File.AppendText("Eq_result.txt"));
// Запись в поток
eq.WriteResult(s);
// Добавка строк в таблицы базы ds
eq.WriteResult(ds);
#endregion
Console.WriteLine("Press any key to restart or esc to save dataset and exit");
}
while (Console.ReadKey(true).Key != ConsoleKey.Escape);
// Условие возвращает true, если не нажата клавиша esc
// Данные базы ds записываются во вновь создаваемый xml-файл
ds.WriteXml("Eq.xml");
}
// Метод ReadKey класса Console срабатывает при нажатии клавиши клавиатуры;
// Параметр true означает, что клавиша не должна отображаться на экране;
// Метод ReadKey возвращает объект структуры ConsoleKeyInfo;
// У этой структуры есть свойство Key.
// Свойство Key возвращает объект перечислимого типа ConsoleKey,
// содержащий все возможные клавиши.
}
}
}
С помощью этого приложения простестируйте работу классов решения квадратного и кубического
уравнений. Далее подумайте над тестирующим windows-приложением.
73
Download