Let’s say I have
interface IMatrix {
double this[int r, int c] { get; }
}
struct Matrix2x2 : IMatrix {
double a1, a2, b1, b2;
double this[int r, int c] { get { ... } }
}
struct Matrix3x3 : IMatrix {
double a1, a2, a3, b1, b2, b3, c1, c2, c3;
double this[int r, int c] { get { ... } }
}
class Matrix : IMatrix { // Any size
double[,] cells;
double this[int r, int c] { get { ... } }
}
Sometimes, instead of just saying
static class Matrices {
static IMatrix Multiply(IMatrix a, IMatrix b) { ... }
}
I end up doing
static class Matrices {
static IMatrix Multiply<T1, T2>(T1 a, T2 b)
where T1 : IMatrix
where T2 : IMatrix { ... }
}
or maybe even
static class Matrices {
static IMatrix Multiply<T1, T2>([In] ref T1 a, [In] ref T2 b)
where T1 : IMatrix
where T2 : IMatrix { ... }
}
to avoid boxing or copying structs.
It works fine and everything, but are there any downsides I don’t know about (other than a negligible increase in memory usage)? Is this an accepted practice, or is it discouraged for any reason I might not be aware of?
Generics come with a small cost, mostly around larger code size. A recent blog post from Joe Duffy gives a fairly detailed look at this. However, in general, avoiding boxing for frequently called code is a good thing and probably worth more generated byte code (in practice, this means slightly higher memory usage and more work for the JIT).