Solving One of the Biggest Array Issues in C#

แชร์
ฝัง
  • เผยแพร่เมื่อ 25 มิ.ย. 2023
  • Check out my new course From Zero to Hero: Logging in .NET and use code LOG20 at checkout for 20% off: bit.ly/loggingdotnet valid for the first 400 purchases!
    Become a Patreon and get source code access: / nickchapsas
    Hello, everybody, I'm Nick, and in this video, I will show you how you can solve the biggest issue with Immutable Array's performance in C# and .NET.
    Workshops: bit.ly/nickworkshops
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: bit.ly/ChapsasGitHub
    Follow me on Twitter: bit.ly/ChapsasTwitter
    Connect on LinkedIn: bit.ly/ChapsasLinkedIn
    Keep coding merch: keepcoding.shop
    #csharp #dotnet

ความคิดเห็น • 79

  • @cdarrigo
    @cdarrigo ปีที่แล้ว +113

    Please do a video on ConfigureAwait. Thanks

    • @MRender32
      @MRender32 ปีที่แล้ว +9

      yeah especially since .NET 8 got a ConfigureAwaitOptions enum overload

    • @peryvindhavelsrud590
      @peryvindhavelsrud590 ปีที่แล้ว +3

      Hopefully ConfigureAwait is soon to be deprecated except for the rare circumstances where you need to use ConfigureAwait(true); which, if my understanding is correct, only applies for UI thread async operations.

    • @axelbreekweg
      @axelbreekweg ปีที่แล้ว +2

      @@peryvindhavelsrud590ConfigureAwait(true) is the default. So if you were to omit this, it’s still always configured this way. And yes, for technologies like WinForms and WPF this is important since it has a UI thread. You need to make sure the continuation task is taken by the UI thread. In these environments ConfigureAwait is relevant, you could use ConfigureAwait(false) if you wish that any other thread than the UI thread can continue the task.

    • @peryvindhavelsrud590
      @peryvindhavelsrud590 ปีที่แล้ว +1

      @@axelbreekweg my point being that it should be possible to alter the default to ConfigureAwait(false) per project, so my libraries by default have the desired behaviour so i don't have to write it everywhere.

  • @MZZenyl
    @MZZenyl ปีที่แล้ว +35

    Unsafe (the class), unsafe (the keyword), P/Invoke, and other topics relating to marshalling and manual memory management, is always good fun.
    For most people, using these is rarely if ever necessary (especially now that we have the Memory and Span structs), but they're a great learning opportunity that can lead to a much deeper understanding of what actually makes .NET tick.

    • @davidmartensson273
      @davidmartensson273 ปีที่แล้ว +1

      Yes, same with reflection, you can cause some very interesting errors :P
      My favorit was "if(a == 0) ..." causing null reference exception. And yes it was the comparisson and no there was not overloaded operators, just comparing and int variable with zero triggered null ref.
      And a was an argument to the function "... MyFunc(int a) { ..."
      You cannot do that without some serious deep diving into the dark corners of C#.

  • @codeyhuntting8830
    @codeyhuntting8830 ปีที่แล้ว +2

    Love the polite frankness at 0:09 - "If you've never used immutable array, then... I'm a bit about that" XD

  • @justwatching6118
    @justwatching6118 ปีที่แล้ว +10

    OFC.. I would really like that you create a (possible longer) video on unsafe :D

  • @sonpham7617
    @sonpham7617 ปีที่แล้ว +48

    It's interesting with ImmutableArray, but it causes potential issues if somebody without these knowledge or by mistake use add or remove method, which creates a whole new array.
    I think IReadOnlyList and IReadOnlyCollection would be better which dont provide add or remove method.

    • @urbanelemental3308
      @urbanelemental3308 ปีที่แล้ว

      I'm in agreement here. I know there can be some performance degradation when accessing an interface and not just the struct, but in this case, I would definitely make a new struct like ReadOnlyArray that makes it clear that the underlying values can change (by the owner) but cannot be changed by the consumer.

    • @anm3037
      @anm3037 ปีที่แล้ว

      I actually never understand the reason for providing all those methods to modify IMMUTABLE Array. IReadOnlyCollection is far better

    • @Rob_III
      @Rob_III ปีที่แล้ว +1

      @@anm3037 An immutable structure is useful for when multiple threads are accessing the items in it for example. You have the guarantee that nobody is modifying the array while a bunch of threads are modifying it. But that doesn't mean you may don't want to modify the array; so there are methods that do that for you by making a copy of the array and meanwhile everything keeps working nicely and it's up to you to decide if/when you want to use the new ("modified") array.

    • @anm3037
      @anm3037 ปีที่แล้ว

      @@Rob_III I disagree with that. Immutable array should not expose such methods. And unfortunately, there is no other contiguous memory that behaves like ReadOnlyCollection.

  • @user-tk2jy8xr8b
    @user-tk2jy8xr8b ปีที่แล้ว +12

    Most of the time, IReadOnlyList is the right type for the right job, not ImmutableArray.
    Btw, several years ago we rewrote some pieces of the application to use Immutable* types. Later, after profiling, we rewrote them back to Dictionary/HashSet/List+IReadOnly* because of poor access time (log vs const) and memory cost (a tree obviously consumes more memory than a hash table, plus worse localization leads to CPU cache misses, but that's more in theory than in practice), both of which were important for the app.
    Immutable arrays are an exception here, because they are optimal by both space and time (even better than IReadOnlyList as interface calls are heavier, with the v-table linear search unless I'm mistaking about that). However, ImmutableArray has one functional downside when compared to IReadOnlyList: it implements ICollection and IList which violates the Liskov substitution principle, so good luck calling `void Add(T)`

    • @fluffydoggo
      @fluffydoggo 11 หลายเดือนก่อน

      Keep in mind ICollection and IList have a IsReadOnly property, so that can be checked for if you really want to

    • @user-tk2jy8xr8b
      @user-tk2jy8xr8b 11 หลายเดือนก่อน

      @@fluffydoggo yes, it's like using one interface for everything in your program, just use IsBehavior1, IsBehavior2 and so on to decide, what methods are supported by a particular instance

  • @peryvindhavelsrud590
    @peryvindhavelsrud590 ปีที่แล้ว +5

    I normally return Read-only Memory or ReadOnlySpan to handle these types. The fact that I'm able to re-retrieve a non Read-only version is a bit sus. The unsafe option is also a possibility for avoiding allocations, not sure if i see any real difference except for that the new Marshalling methods make it even easier to get the mutable array back.

  • @F1nalspace
    @F1nalspace ปีที่แล้ว +16

    I have encounter that issue several times and always wanted a immutable array that just point to the backing array, so really thanks for sharing that ;-)
    I didn´t knew Unsafe.As() either... i really need to look into this, because i like unsafe stuff ^^

    • @FireDragon91245
      @FireDragon91245 ปีที่แล้ว +6

      if you like unsafe i have a neat fun fact you can actualy do
      unsafe class GenericPointer where T : unmanaged
      {
      public T* Ptr;
      }
      its posible to make generic pointers

    • @neociber24
      @neociber24 ปีที่แล้ว +2

      Dangling pointers are so much fun

  • @Stdvwr
    @Stdvwr ปีที่แล้ว +1

    Why would you create an array just to cast it to immutable array? Why not ToImmutableArray directly from the enumerable? What about ImmutableArray.Builder?

  • @zwatotem
    @zwatotem ปีที่แล้ว +3

    I just noticed the background image of your logging course 😂

  • @cn-ml
    @cn-ml ปีที่แล้ว +3

    Honestly, i think the problem is a different one if your need to use this. If your application requieres you to access the backing field of the immutable collection, you shouldnt have used an immutable collection in the first place. Also, if your application requieres a fast immutable collection access without reallocation, i think you should make that clear using the api interface, using the AsReadOnly methods.

  • @the-niker
    @the-niker ปีที่แล้ว

    Good stuff I can see it being used in cases where a method creates and fills an array dynamically but returns immutable version of it in the end and discards the original for zero additional cost.

  • @phoenixwyllow
    @phoenixwyllow ปีที่แล้ว +2

    I believe quite a few libraries use the ToImmutableArray stuff to generate things once and some use it for exactly the purpose of ensuring the internal array doesn't mutate.
    I would love to have a clean immutable or readonly implementation specifically for initalizing (avoiding the creation of the first mutable array) so we don't have to resort to unsafe or Unsafe calls...
    I suppose in some cases it may just be worth forcing a breaking change in favour of ReadOnlyList, but sometimes that's just not possible or simply the wrong approach...

  • @kingumosama
    @kingumosama ปีที่แล้ว

    It was very helpful information. Thank you so much.

  • @MRender32
    @MRender32 ปีที่แล้ว +1

    nice! i thought this would be about collection literals, pleasantly surprised

  • @kpamafrederic6558
    @kpamafrederic6558 ปีที่แล้ว +3

    All right. I'm definitely gonna need that video on Unsafe. I failed at converting a ReadOnlySpan to ReadOnlyMemory without new allocation 2 times now

  • @inzyster
    @inzyster ปีที่แล้ว

    Interesting stuff, definitely useful for some of my projects.

  • @YuraSuper2048
    @YuraSuper2048 ปีที่แล้ว +1

    Please make a video about unsafe it seems very fun

  • @JustinLampe
    @JustinLampe ปีที่แล้ว +1

    A new Rider EAP with AI integration. We need a video for this!

  • @justwatching6118
    @justwatching6118 ปีที่แล้ว +1

    Yeah.. I've seen a LOT of times people over abusing lists (use them for every type of collection)..

  • @feefifofum6383
    @feefifofum6383 ปีที่แล้ว +1

    So is it quicker to loop over immutable arrays compared to normal arrays?

  • @fehmidenguir6278
    @fehmidenguir6278 ปีที่แล้ว

    Hello Nick, thanks for the video. I wanted to ask how you can access the method implementation of the dotnet framework through your IDE ? Is it a plugin or it's supported out of the box. Also is it supported in Visual Studio

    • @nickchapsas
      @nickchapsas  ปีที่แล้ว

      It is built in out of the box in Rider

  • @nanvlad
    @nanvlad ปีที่แล้ว +2

    What if I use users[0] = myVar? Does it create a new array for that or just replace first item with a new value?

    • @iamprovidence-xj5wf
      @iamprovidence-xj5wf ปีที่แล้ว +2

      It does not have setter, so such operation is just not possible

  • @Eduardoo
    @Eduardoo ปีที่แล้ว

    I'm surprised that frozen collections weren't mentioned in the video

  • @Dustyy01
    @Dustyy01 ปีที่แล้ว +4

    More unsafe stuff🎉

  • @leandroteles7857
    @leandroteles7857 ปีที่แล้ว +9

    It's worth noting that this only works because ImmutableArray is a struct. (An object reference and "a struct with an object reference" have the same memory representation)

    • @manilladrift
      @manilladrift ปีที่แล้ว +2

      Thanks for explaining!

  • @jonasgranlund4427
    @jonasgranlund4427 ปีที่แล้ว

    Since It is so many comment regarding the IReadonlyList... instead of an ImmutableArray I have a feeling we will get a post about those in a near time :)

    • @nickchapsas
      @nickchapsas  ปีที่แล้ว +3

      I think I'll do a video about all these types because people seem to largely misunderstasnd them and misuse them

  • @alexanderkvenvolden4067
    @alexanderkvenvolden4067 ปีที่แล้ว

    Does this do the same thing as using reflection to call the internal constructor? I suppose the advantage to that is not relying on non-supported means which Microsoft could break without notice.

    • @phizc
      @phizc ปีที่แล้ว

      Another bonus is that it avoids boxing the result. Using reflection would return the ImmutableArray as an Object.

  • @urbanelemental3308
    @urbanelemental3308 ปีที่แล้ว

    I feel like it should be a different struct/class that is a wrapper around the array like a ReadOnlyArray or something like that. Allowing 'hacks' like this seems to break the intention.

  • @neociber24
    @neociber24 ปีที่แล้ว

    I crashed programs so many times using unsafe that's how you know is fun 😂
    I feel weird those marshal methods are not marked as unsafe

  • @Lubgi3
    @Lubgi3 ปีที่แล้ว

    Can’t we return ReadOnlySpan for that purpose? Is there any difference?

    • @nickchapsas
      @nickchapsas  ปีที่แล้ว +1

      ReadOnlySpan is a ref struct so it can only be stack allocated which limits where it can be used

  • @EddieDemon
    @EddieDemon ปีที่แล้ว

    Haha, “tl;dw” 😂

  • @lordshoe
    @lordshoe ปีที่แล้ว +2

    So, how is it actually any more immutable than before if you can just get the backing array anyways? Or is it mostly just for the convenience it offers?
    I've never used ImmutableArray, but what benefits does it provide over exposing something like an IReadOnlyCollection?

    • @protox4
      @protox4 ปีที่แล้ว +2

      It's faster. Interfaces make iterating much slower.
      Those are unsafe methods, so you should not be using them in general code. They are safe if you use them correctly.

    • @nickchapsas
      @nickchapsas  ปีที่แล้ว +8

      It's the safety net it provides. You could always do something like user reflection and hack your way around the problem but that would have been inefficient. ReadOnly data structures and Immutable data structures are very different in C#. Maybe they deserve a video of their own

    • @yjagota
      @yjagota ปีที่แล้ว +7

      @@nickchapsas I had the same question. A video on benefits of ImmutableArray over List or IReadOnlyList will be great. And thanks for all the great videos Nick. Keep up the good work.

  • @figloalds
    @figloalds ปีที่แล้ว +2

    I don't subscribe to this, to me this is like an extra pair of wheels on the bike of programmers who are bad
    And if I'm saying "ToImmutableArray" and this makes a whole copy, then it's not very different from handing back a modifiable copy array which the user function may need to alter anyways in it's own processing
    And besides, almost everything in OOP is references and I really doubt the "immutable" array makes the underlying objects any immutable, it's still shared state anyways, still requires common sense from the programmers writing the lib and user code

  • @SeanAlunni
    @SeanAlunni ปีที่แล้ว

    If the "ImmutableCollectionsMarshal" actually calls the internal constructor, how does it manage to not allocate any byte?

  • @Crozz22
    @Crozz22 ปีที่แล้ว +1

    I don't understand the point of this, you can just pass around an IReadOnlyList instead. Arrays implement IReadOnlyList. Considering you have to be careful to not change the backing array/list anyway with both approaches.

    • @nickchapsas
      @nickchapsas  ปีที่แล้ว +1

      Intent is the point. Why would you use a ReadOnlyLIST to represent an array that cannot be mutated and why would you give up on the ability to generate new immutable versions of that data structure by not using ImmutableArrays?

    • @chris-pee
      @chris-pee ปีที่แล้ว +4

      @@nickchapsas Just because .NET collection interfaces make no sense whatsoever, doesn't mean Immutable represents the intent better (I don't think it does).

  • @kaiserbergin
    @kaiserbergin ปีที่แล้ว

    feels like ReadOnlyArray - but I'll take it!

    • @danyafulenwider8530
      @danyafulenwider8530 9 หลายเดือนก่อน +1

      =

    • @danyafulenwider8530
      @danyafulenwider8530 9 หลายเดือนก่อน

      😊 why does my array embroidery machine just stop in the middle of a project😊 0:00 0:00

  • @AlFasGD
    @AlFasGD ปีที่แล้ว +1

    And as always, keep coding unsafely

  • @Palladin007
    @Palladin007 ปีที่แล้ว +1

    Honestly, what is the point of the ImmutableArray? Why not just IReadOnlyList?
    Well, you can cast back, but there is also a way to manipulate the ImmutableArray.
    Or what speaks against ReadOnlyMemory?

  • @paarma1752
    @paarma1752 ปีที่แล้ว

    I have found that more often than not the exact requirement is not to map the dto from a single object, but actually from multiple. More often than not it's best to keep the dtos as the API for your domain logic (your domain logic digests a dto and then outputs another one). And this also means that your dtos should be part of your domain.

  • @user-zk5ym9ut1j
    @user-zk5ym9ut1j ปีที่แล้ว +1

    Never seen any practical application of ImmutableArray for 7 years, a bit of HashSets here, a couple Queues there, but ImmutableArray is a bit confusing collection

  • @ryanzwe
    @ryanzwe ปีที่แล้ว

    :o

  • @FireDragon91245
    @FireDragon91245 ปีที่แล้ว +1

    the only valid array type is T* where T : unmanaged

  • @Bliss467
    @Bliss467 ปีที่แล้ว

    Why do we use properties with public get set instead of just a public field? It seems like a completely superfluous wrapper

  • @Mitakbacktrack
    @Mitakbacktrack ปีที่แล้ว

    I think we can also use Array.AsReadOnly(_users). It uses 24 bytes for 10,000 iterations. It's slower - 2.4 ns vs. 0.0082 ns on the Smart() method.

  • @anomalii7720
    @anomalii7720 ปีที่แล้ว

    So you have an immutableArray, you copy into a mutable Array and you have the same problem.

  • @dmitrykim3096
    @dmitrykim3096 ปีที่แล้ว

    I think you had to choose c++, you spend a lot of time on a lower level optimizations and hacks with C#.

  • @robbielowe2516
    @robbielowe2516 ปีที่แล้ว

    🌸 Promo'SM

  • @dukefleed9525
    @dukefleed9525 ปีที่แล้ว +2

    ImmutableCollectionMarshal.AsArray looks the best way ever to shoot yourself in the foot.

  • @mrwensveen
    @mrwensveen ปีที่แล้ว

    My method isn't quite as fast as with Unsafe or ImmutableCollectionsMarshal trickery, but it is quite fast compared to AsImmutable: ImmutableArray.Empty.AddRange(_users!);
    About a 1000 times as fast and doesn't allocate memory (speed is constant so 100 or 10_000 items is similar). And it's totally safe! I swear!

  • @nataliasikorka4825
    @nataliasikorka4825 ปีที่แล้ว +1

    I have never encountered this issue, because IReadOnlyList exists :)