Hacker News new | ask | show | jobs
by Pannoniae 767 days ago
No, this is still a thing (and a performance issue) in both Java and C#. Covariance has a substantial penalty on array writes (20-40% depending on the benchmark).

I'm not that familiar with Java, but in C#, the only ways to avoid the penalty are either making the class of the array type sealed (so the runtime knows that you can't put any subtype into it) or using a construct like this if you work with someone else's type which you can't make sealed:

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  private static T access<T>(T[] arr, int index) {
      ref T tableRef = ref MemoryMarshal.GetArrayDataReference(arr);
      return Unsafe.Add(ref tableRef, index);
  }
(this doesn't bounds check either, and hard-crashes on an empty array so you need to guard it appropriately)
1 comments

This does not hard-crash on an empty array[0] but can still cause bad UB and eventually crash should it point past allocated memory page or to other random data (spec allows to have a byref that would point to the last element of array if it were one element longer, but such dereference out of bounds is still UB).

Covariance is unfortunate choice of arrays of T where T is class and is widely considered a mistake today. When you pass Memory<T> or Span<T>, there are no covariance checks involved as they disallow it.

It is also less of an issue in .NET in general because quite often T is a struct instead, which does not have covariance in the OOP meaning of the word (old-style int[] to uint[] casts are just reinterprets, they are frowned upon luckily and few codebases use them).

[0] https://sharplab.io/#v2:EYLgtghglgdgNAFxAJwK4wD4AEBMBGAWACgs...