Zacznę może od tego, że potrzebuję w pewnym projekcie pisanym w C# obsługę macierzy (dodawanie, mnożenie, ślad, itp). Konkretniej chodzi o macierz skonstruowaną z elementów typu prostego Double, ale dobrą cechą byłaby jej uniwersalność (czyli obsługa dowolnych typów) – w skrócie macierz generyczna. Ponieważ jestem troszku leniwy więc rozpocząłem poszukiwania odpowiednich bibliotek w Internecie. Niestety jedynym rozwiązaniem godnym przejrzenia, aczkolwiek niespełniącym moich wymagań była biblioteka CSML. Wadą jej polega na tym, że natywnie operuje na liczbach zespolonych (co można oczywiście zarzutować w przestrzeń liczb rzeczywistch) i nie ma domyślnie zrobionej konwersji Double na Complex.
Zdziwiło mnie, że taka ważna struktura w obliczeniach numerycznych nie dorobiła się pożądnej implementacji (dodam jeszcze: darmowej).. No cóż, w takim razie należy samemu napisać odpowiedni kod.
Zacząłem od stworzenia szkieletu klasy:
public class Matrix< T >
{
T[,] matrix;
public int N { get; private set; }
public int M { get; private set; }
public Matrix(int n, int m)
{
}
public T this[int i, int j]
{
}
public static Matrix< T > operator +(Matrix< T > A, Matrix< T > B)
{
}
public static Matrix< T > operator -(Matrix< T > A, Matrix< T > B)
{
}
public static Matrix< T > operator *(Matrix< T > A, Matrix< T > B)
{
}
}
Dokonałem następnie implementacji pierwszego z operatorów:
public static Matrix< T > operator +(Matrix< T > A, Matrix< T > B)
{
if (A.N == B.N && A.M == B.M)
{
Matrix< T > result =
new Matrix< T >
(A.
N, A.
M);
for (int i = 0; i < A.N; i++)
{
for (int j = 0; j < A.M; j++)
{
result[i, j] = A[i, j] + B[i, j];
}
}
return result;
}
else
{
throw new Exception
("Matrices have different dimensions");
}
}
Próba kompilacji i... błąd: "Operator '+' cannot be applied to operands of type 'T' and 'T'". W sumie prawda, skąd kompilator ma wiedzieć, czy typ T ma zaimplementowany operator dodawania. Właśnie po to wprowadzono system reguł mogący wymusić na danym typie kilka cech. Zaglądam więc do dokumentacji MSDN i niestety nic ciekawego nie znalazłem. Odpalam wyszukiwarkę dotarłem do kilku wątków o tym samym problemie i jedyną odpowiedzią jest to, że: nie można czegoś takiego zrobić ponieważ Double jest typem prostym.
Ostatecznie jest jeszcze jedno rozwiązanie ("Using generics for calculations"), ale moim zdaniem nie do końca jest ono wzorowe z tego względu, że należałoby stosować dodatkową warstwę pomiędzy typami Int32, Double a naszą klasą generyczną. Ale dużego wyboru nie ma..