8

C#: The fact that T[,] (multi dimensional arrays) implement IEnumerable, but not IEnumerable<T>.

But one dimensional arrays do.

But you can still foreach over them.

What in the actual hell?

Now I have to write something ugly like
var bruh = values.Cast<float>();

Comments
  • 1
    Note that bruh's type is IEnumerable<float>
  • 3
    Using Cast<T> will actually type cast every single element....

    Jesus.
    Christ.

    https://github.com/dotnet/runtime/...
  • 5
    Yes that's so unfortunate, on a larger data-set having a cast on each object would have a visible impact on performance. Some shortcomings of the language, I guess the developers where either blind or just couldn't c#
  • 0
    Guess you might want to write a library that fixes this. Y'know.
  • 7
    Use arraylist or jagged arrays instead. When you're enumerating a multidimensional array, you're not enumerating a collection of n T[], you're enumerating a positional iterable. Jagged arrays are enumerating typed internal arrays, arraylist is basically the same thing with some additional mechanics.
  • 3
    [The text is nil]
  • 0
    @SortOfTested Jagged arrays are also nice for performance, but I'm using a library that expects me to put in a multi dimensional array.

    (that gets converted to an opencv mat, not sure if directly or indirectly)
  • 1
    @LotsOfCaffeine
    That's an odd decision, I wonder if it's old or a port of another language impl. Usually nowadays you'd just use an enumerable of tuples for that. Significant perf improvements.
  • 0
    @SortOfTested I mean, is it that much of a performance boost?

    I mean unless you're dynamically resizing the multidim array, shouldn't it just be some modulo operations for the index?
  • 1
    @LotsOfCaffeine
    With tuples you can just stream it all the way down and not have to deal with the intermediary array box.
  • 1
    [streamey cam codes]
  • 0
    @SortOfTested the new-ish tuple syntax is really nice, but not often a proper replacement for arrays.

    I'm doing image processing so my matrices/arrays are hundreds of pixels long.

    btw, what IDE/editor are you using to get those fancy notes in the code?
  • 2
    @LotsOfCaffeine
    I will say that tuples are more than a syntax, they're value types. They are designed to expire from memory as fast as possible and have miniscule weight. Span follows the same strategy.

    If you need the buffer to be persistent or contains giant arrays of data, then they're not that useful.

    The ide above is rider. It's excellent.
  • 0
    @SortOfTested I see, it looks kinda nice.

    Not sure if I'd personally use those additional annotations though.

    Also I am kind of judging you for using "var i" in a for loop instead of "int i"...
  • 1
    @LotsOfCaffeine
    Feel free, this isn't java. .Net coding conventions states developers should prefer var when the type can be inferred.

    https://docs.microsoft.com/en-us/...

    "Use implicit typing for local variables when the type of the variable is obvious from the right side of the assignment, or when the precise type is not important."
  • 0
    @SortOfTested yeah, I never really used war. The teacher I had didn't mention it until later in the class, with the goal that we learn the differences between types.

    I do use var for long types though, no point in writing

    IEnumerable<KeyValuePair> something = ...;
  • 3
    @LotsOfCaffeine
    Whatever language I'm writing, I just follow the established convention, I don't have strong opinions about things which are subjective.

    I've been using C# since .Net was in alpha, so I just know where the skeletons are buried.
  • 0
    @SortOfTested interesting

    I very much like C#, some things feel a bit off though I'm not gonna lie.

    Some aspects of the collections are unexplainable to me. Like how Arrays technically implement ICollection<T>, but can't use the Add method and it's hidden with some dark sorcery.

    And I'm still not fully convinced on Span<T>
  • 1
    @LotsOfCaffeine
    It's because all that is part of the 1.0 collections history. The specialized namespace is where most of that is hidden, but some bleeds out.

    https://docs.microsoft.com/en-us/...

    Span<T> is proved out in the perf enhancements it brought to the framework. The overall string manip perf went up 45% after they converted all the underlying impls to use span.
  • 1
    @SortOfTested I still don't get how Span improves performance?

    It's just a pointer and a length, right?

    Shouldn't that just be the same as an array, but as a value type?

    And since structs don't implement interfaces, you can't use Span<T> when expecting an IEnumerable<T>, but you instead have a special struct for enumerating Spans.

    Don't get me wrong, I'm all for optimization, and please correct me if I got something wrong in this reply, but the recent changes to C# make the language and eco system... a bit convoluted. But maybe that's just me.

    The specialized namespace is interesting. I wonder why StringCollection is a thing, surely it's optimized in one way or another, otherwise you'd just use a List<string>.
  • 3
    @LotsOfCaffeine
    It's not really an array-contextual type, it's memory contextual. Arrays represent series of pointers to some other type. Span<T> represents a single pointer to a range of memory that is subordinate to some other series T (1 vs n).

    They're not intended to be walkable, they're intended to be used as is.

    Consider extracting the last space delimited value from a string:

    "Some Value String"

    substr method:
    1. Expose the byte array
    2. Find the last space index in string
    3. create a temp string
    4. walk array to copy the values into that string
    5. return the new string

    Span<T>:
    1. Find last space index
    2. Extract memory range using cursor from last space index (string -> Span<byte> is implicit)
    3. Return span, coerce to string as needed

    Joins/concat are fast as well, because you can just take the joinable members, get the size, alloc a string and use CopyTo. Finalize to a single string.

    These ops are unsafe, and usually twice as fast as the object-relative operations.
  • 0
    So glad I write Ruby and C.
    Ruby does all of the object handling for me.
    C lets me do it all myself without getting in the way.
  • 0
    @SortOfTested shouldn't an array of value types just be a pointer to a span of memory containing all the values inline?
    So let's say you have an array of bytes representing a string. If you want to get a substring you just point to a different point in that byte array with a different length?
Add Comment