Liczby zespolone w C#

Projektowanie klas nie jest zagadnieniem, które podczas nauki języka C# należałoby odkładać ''na później''. Oczywiście środowiska programistyczne, tworząc za nas szkielet kodu aplikacji, pozwalają do pewnego stopnia uniknąć definiowania własnych klas. Jednak bez podstawowej choćby orientacji w tej materii mogą pojawić się kłopoty nawet z pełnym zrozumieniem rozwijanego przez nas programu.

Projektowanie klas nie jest zagadnieniem, które podczas nauki języka C# należałoby odkładać 'na później'. Oczywiście środowiska programistyczne, tworząc za nas szkielet kodu aplikacji, pozwalają do pewnego stopnia uniknąć definiowania własnych klas. Jednak bez podstawowej choćby orientacji w tej materii mogą pojawić się kłopoty nawet z pełnym zrozumieniem rozwijanego przez nas programu.

Niniejszy artykuł ma przybliżyć praktyczne zagadnienia związane z definiowaniem typów. Jest to w istocie studium przypadku, w którym pokazujemy krok po kroku drogę do utworzenia w pełni funkcjonalnej struktury implementującej liczby zespolone.

Implementacja liczb zespolonych

Rysunek 1. Kreator projektu ze środowiska Microsoft Visual C# .NET 2003.

Rysunek 1. Kreator projektu ze środowiska Microsoft Visual C# .NET 2003.

Zarówno wśród klas Javy, jak i platformy .NET brakuje tak podstawowego w niektórych zastosowaniach typu, jak liczby zespolone. To zadziwiające, zwłaszcza że w tak "starym" języku, jak FORTRAN, ten typ był zaimplementowany już w pierwszej edycji. W poniższym artykule postaramy się uzupełnić ten brak.

Osobom, które nie znają liczb zespolonych, należy się słowo wyjaśnienia. Liczby zespolone zostały wprowadzone do algebry w XVI wieku, aby znaleźć dziedzinę, w której wszystkie równania kwadratowe mają rozwiązania. Łatwo się przekonać, choćby szukając punktów zerowych dwumianu x2+1, że nie zawsze znajdziemy je wśród liczb rzeczywistych. Liczby zespolone składają się z części rzeczywistej i urojonej z=a+ib, przy czym jednostka urojona i jest pierwiastkiem z liczby -1. Dzięki liczbom zespolonym, wprowadzonym do matematyki na dobre w 1748 roku przez Eulera, łatwiej rozwiązać wiele zagadnień matematyki, fizyki i techniki. Dziś są one jednym z podstawowych narzędzi matematyki, uczy się o nich już w szkołach średnich. Niestety, jak widać, nie znajduje to jeszcze odzwierciedlenia we wszystkich językach programowania.

Przejdźmy do zagadnień praktycznych. Pierwsza decyzja, którą musimy podjąć, to wybór między strukturą a klasą. Na szczęście w wypadku implementacji liczb wybór jest raczej oczywisty - powinniśmy utworzyć strukturę. Zdefiniujmy zatem strukturę Complex, która będzie implementacją typu liczb zespolonych. Każda jej instancja (obiekt) będzie przechowywała wartość konkretnej liczby (w .NET 2.0 moglibyśmy rozważyć użycie generic type, czyli odpowiednika szablonów znanych z C++. Niestety, obecna wersja platformy nie daje jeszcze takich możliwości).

Projekt i plik struktury

Rysunek 2. Okno udostępniające zbiór szablonów plików, które możemy dodać do projektu.

Rysunek 2. Okno udostępniające zbiór szablonów plików, które możemy dodać do projektu.

Tworzymy nowy projekt. W tym celu w Microsoft Visual C .NET 2003 naciskamy kombinację klawiszy [Ctrl Shift N], w oknie widocznym na rysunku 1 wybieramy typ projektu Windows Application i ustalamy jego nazwę na Zespolone. Następnie do tego projektu dodajemy ([Ctrl Shift A]) plik typu Class. Zanim w oknie Add New Item, widocznym na rysunku 2 klikniemy przycisk Open, zmieńmy w polu Name nazwę pliku na Complex.cs. Po wykonaniu tych czynności zobaczymy w edytorze moduł zawierający definicję najprostszej klasy z konstruktorem domyślnym:

using System;

namespace Zespolone

{

/// <summary>

/// Summary description for Complex.

/// </summary>

public class Complex

{

public Complex()

{

//

// TODO: Add constructor logic here

//

}

}

}

My jednak postanowiliśmy zdefiniować strukturę. Wobec tego pierwszą czynnością będzie zmiana słowa kluczowego class na struct. Następnie uzupełniamy powyższy "szkielet" definicjami pól, metod i własności zgodnie z poniższym wzorem:

using System;

namespace Zespolone

{

/// <summary>

/// Implementacja liczb zespolonych

/// dla platformy .NET

/// </summary>

public struct Complex

{

public double Real,Imag; //pola

//konstruktor

public Complex(double real, double imag)

{

this.Real=real;

this.Imag=imag;

}

#region Metody

public void Conj()

{

Imag=-Imag;

}

public override string ToString() //metoda

{

return "("+Real.ToString()+","+

Imag.ToString()+")";

}

public string ToStringI() //metoda

{

string s=Real.ToString();

if (Imag>=0) s+="+i"; else s+="-i";

s+=Math.Abs(Imag);

return s;

}

public override bool Equals(object obj)

{

if (obj is Complex)

{

Complex z=(Complex)obj;

return (this.Real==z.Real && this.Imag==z.Imag);

}

else return false;

}

public override int GetHashCode()

{

return

Real.GetHashCode()^Imag.GetHashCode();

}

#endregion

}

}

Do klasy dodaliśmy dwa pola, zmodyfikowaliśmy konstruktor w taki sposób, aby pozwalał na ich zainicjowanie, oraz dodaliśmy zbiór metod, z których część zastępuje metody z klasy bazowej, którą dla struktur jest System.ValueType.


Zobacz również