6

I am writing two classes in C#:

  • A Matrix class that represents a general Matrix with n-by-m dimensions
  • A SquareMatrix class that inherits from Matrix and has the constraint of being n-by-n

The reason I designed it this way is because square matrices support additional specific operations like calculating the determinant or the inverse, so being able to guarantee that those functions are avaliable with the specific type you're using are nice things to have. Additionally it would support all the regular Matrix operations and can be used as a Matrix

I have a function in Matrix called getTranspose(). It calculates the transpose of the Matrix and returns it as a new Matrix

I inherited it in SquareMatrix, but because the transpose of a square matrix is guaranteed to be square matrix, I also want it to return a SquareMatrix

I am unsure about the best way to do this.

  • I can re-implement the function in SquareMatrix, but that would be code duplication because it's essentially the same calculation
  • I can use implicit typecast operators, but if I understand correctly that would cause unnecessary allocations (upcast SquareMatrix to Matrix, create a new Matrix as the transpose, create a new SquareMatrix during typecasting and throw away the tranposed Matrix)
  • I can use explicit typecast operators, but it would be stupid to have to typecast the transpose of a SquareMatrix explicitly, and it also has the same problem of the implicit operator with unnecessary allocations

Is there another option? Should I change the design of having SquareMatrix inherit from Matrix?

This problem also applies to operators. It seems that I have to either implement typecasting operators which might cost in performance, or have to re-implement the same code.

  • 1
    This is called covariance. Have you tried to write the SquareMatrix version of getTranspose() to return a SquareMatrix? – andy256 Aug 27 '14 at 2:33
  • I haven't actually written it yet, but if I did, I would need to basically copy paste the same code as getTranpose() from Matrix, which I'm trying to avoid. – 9a3eedi Aug 27 '14 at 2:39
  • 1
    Just call the Matrix method and return the result as a SquareMatrix. – andy256 Aug 27 '14 at 2:43
  • 3
    Are matrices immutable? There is a very important reason why this matters. – user22815 Aug 27 '14 at 3:42
  • They are not completely immutable in my implementation, as in the values of the elements may change. However, the dimensions do not change once created. – 9a3eedi Aug 27 '14 at 4:28
6

Inheritance not helping to eliminate repetition and typecasts is often a sign that generics would help. You can do something like:

public T getTranspose<T>()
// or non-member function
T getTranspose<T>(T input)

I haven't fully worked it out, but it seems it might get awkward on the calling side. I know C# does some inference with generic methods, but I don't know C#, so I'm not familiar with the details. That might be the way you have to go, though, if you want full compile-time type checking with the least amount of repetition in the implementation.

Another option would be to create private helper functions, then pass in the result type you want, for the helper to populate, like:

public SquareMatrix getTranspose() {
    SquareMatrix result = new SquareMatrix();
    transposeHelper(result);
    return result;
}

This gives you more boilerplate on the implementation side, but at least it isn't full repetition.

A third option is just to check if the result is square in the Matrix implementation, and return a SquareMatrix if it is, like:

public Matrix getTranspose() {
   Matrix result; 
   if (resultIsSquare())
        result = new SquareMatrix();
   else
        result = new Matrix();

   // calculate result
   return result;
}

This has the advantage of not needing any implementation at all for getTranspose() in SquareMatrix, but at the expense of requiring type checking of the return value at the call site. It also works for cases like multiplying two non-square matrices that happen to give a square result. You give up most compile-time type checking, though.

If your application happens to mostly require run-time instead of compile-time type checking anyway, you might as well just give up the different types and throw an exception if you call a method that a non-square matrix doesn't support. I believe this is the approach most existing libraries take, especially since there are other conditions than being non-square that can cause methods like inverse() to fail.

Speaking of libraries, there are a lot of good ones out there for matrix math, that are already heavily tested and optimized. Don't reinvent the wheel if you don't have to.

  • The second option seems best. I would put the transpose helper function in Matrix as a protected function so that inherited classes can use it, but not outside. Another problem with the third option, from what I can see, is that this would make the base class depend on the derived class, which seems ugly. I also considered giving up having multiple types. It's a valid option due to the reason you mentioned. As for using libraries, I actually was using Math.NET, until I realized how unnecessarily complex it was and it didn't serve 50% of my needs despite the complexity. – 9a3eedi Aug 27 '14 at 5:24
  • Another interesting idea I thought of while using helper functions is the use of lambdas to reduce code repetition even more. This is very useful for implementing operators, from what I saw, by implementing a helper function that takes in a lambda and iterates through the matrix while performing the calculation specified in the lambda. – 9a3eedi Aug 29 '14 at 1:23
  • This does not solve anything. Generics are not an option because T would have to be the class type itself. You cannot define a class T<T> . The second won't work because the method signature will clash with the existing getTranspose. The third idea just returns Matrix again. – Martin Maat Oct 2 '19 at 6:19
  • If you can implement it with only the public members of Matrix, you could implement it as an extension static T getTranspose<T>(this T mat) where T : Matrix – Caleth Oct 2 '19 at 11:10
2

The problem is that the concept of "this type" is missing in C#. It can be simulated but it uses a syntax a bit complex or confusing, and I would not advise using it. The question below describes such an implementation.

https://stackoverflow.com/questions/1400831/is-it-possible-to-make-this-type-for-generics-in-c

2

Use the "new" operator in the inherited function to override the output and call the base class using the "base" operator.

Something like:

public new SquareMatrix getTranspose()
{
    // Do something different here... or not...
    return (SquareMatrix)base.getTranspose();
}
1

The performance penalty for casting you are suggesting is not real, nothing is re-allocated if you cast a base type to a derived type. So you can safely do this on the receivers end. If the client code knows it will get a richer type for sure because it created the derived class, it can cast the returned type to the richer type, preferably using the "as" operator. This is not bad practice at all.

If you are really bugged by the cast in client code you could add an extra method in the derived class (with a slightly different name). You would not need to reimplement anything as you suggest, you would call base.GetTranspose and do the cast before the return.

Generics would not help a lot here since the T type would have to be the class being implemented rather than some variant type being handled by it. This is impossible.

Alan's suggestion is a hack, it would kill the virtuality of the method for descendants.

But here's the kicker: your idea of using a derived class to implement a constraint is plain wrong. It violates LSP and is the root cause of your very problem. You should just have a property IsSquare instead on your base class.

Not the answer you're looking for? Browse other questions tagged or ask your own question.