In case anyone wonders about the line using the -> operator: pointer->ToString() is the same as (*pointer).ToString() So, it means, "get the int from pointer and call ToString() on it"
Minor complaint about the the thumbnail. I'd love to be able to see these commands before I click on the video instead of them being blurred out, it'd peak my interest and give that effect of like "oh what does that keyword do"
i've had to use all but one of them, i've never used stackalloc, but since i'm working with C# to C++ interop, these concepts are actually used quite frequently :P
@@nickchapsas I use interop in mobile xamarin projects, we have some library implementing digital signature and encryption in GOST algorhytm, it is implementing Microsoft C++ CryptoApi, only way to use it on mobile is via interop. Only things I use for it is Marshal class and sequental layouted structs.
@@nickchapsas well i'm writing a C++ library for unity, so most of these things have at least cropped up at some point, though we do our best to limit it, since it does add a lot of complexity
@@nickchapsas Then I'd recommend you look into the Stride Engine. A really buggy and fragile mess but it's mostly 90 - 99% written in C# and some basic bare bones C++.
I've used sizeof a few times, it's useful when implementing low level integrations that require fixed package sizes. When simulating a TCP/IP protocol for instance.
In 'normal' or usual C# (C# only development) majority of these keywords probably wouldn't be used. But if your C# program works with unmanaged code (C++, Fortran...) these keywords are pretty useful. For example sizeof is used for types interoperability. You must be specially careful when mixing managed and unmanaged code. Another great video, Nick!
2:17 - casting an IntPtr to an int under 64-bit where that int would overflow will also throw an OverflowException, as the conversion occurs in a checked context. Using unchecked will also silence some overflow warnings, e.g. when working with the lparam in WndProc. 5:34 - that's a 64-bit pointer, so 6:39 you probably want to cast to a long, as casting to an integer will chop off the upper 32 bits of the 64-bit pointer. 9:16 - C# strings are not null terminated - the implementation just happens to put zeroes in the slack space after the end of the string.
In the C# language specification, section "22.7 The fixed statement" it says: A char* value produced by fixing a string instance always points to a null-terminated string. Within a fixed statement that obtains a pointer p to a string instance s, the pointer values ranging from p to p + s.Length ‑ 1 represent addresses of the characters in the string, and the pointer value p + s.Length always points to a null character (the character with value '\0').
I don't think volatile is applicable to this topic. I completely agree about all that unsafe stuff, it is almost never needed even if you sometimes work with unmanaged code like me. But volatile is not about low level code, it is about thread-safety in high level code, many people use it because it is more convinient way than explicitly using Interlocked class.
@@iGexogen The topic isn't about low level code. It's about keywords that you don't usually see used in C#. I just happened to have grouped many low level stuff in one video to help it be easily consumed.
@@nickchapsas It will be interesting to see how many of commentators actually use volatile, I think that unity guys must be using advanced multi-threading. It was suprising for me that you never needed it, I suppose lock statements and concurrent collections is enough for all your async purposes. Waiting for part 2)
I want to point out something about pointers in C#: in C#, the star is part of the type. In C++ and C, the star is part of the declarator. What does this mean? In C#, "int* a, b;" will declare two int pointers. In C++, "int* a, b;" will declare an int pointer and an int. This is why we align stars and ampersands (for references, I'm not talking about the adress-of operator) to the right in C++. Because it is part of the declarator: "int *a, b; int *x, *y;" So if someone puts the star on the left in C++, that doesn't mean they have different style taste, it just means they don't understand how it actually works.
I agree mostly, with the exception of your last remark. I use right-aligned operators too, but I also understand why people use left aligned operators. Especially in businesses where multiple declarations in a single line are forbidden in their coding standard (a lot of companies have this as a rule). I would argue that aligning them to the left can make it a lot more clear to the reader what a variable or member can contain, there is a lot of debate of semantics versus technicality here. Saying someone does not understand how it works because you do not agree with their choices, is not a constructive point. I have had people that designed compilers explain to me why they used left aligned.
@@BGroothedde I guess I was being a little too general here. But 90% of the time, people put it on the left because they don't understand the implications of it. Maybe not in a job setting, but when people make the decision themselves. We can argue about good practice, but that never ends well. My stance on style is just to do what the project you're joining already does, so if a company puts the star on the left, I'll do that too. But if you see a star on the left in a personal project, it's an _indication_ that they lack some understanding of the language. But frankly, I was unaware that some companies do it, because it is imo just incorrect, the star belongs to the declarator. But if you don't ever group declarations (which I also don't do), then it might not matter so much.
Saying the star is not part of the type in c++ is nonsense. If that was the case, std::is_pointer and std::is_pointer should return the same value. Obviously they don’t and there are plenty of other examples where in type contexts whether something is a pointer type or not matters. I get what you are saying about multiple variable declarations, but I would contend that is a defect of the parsing rules of the language. In all other cases, you need to consider pointer/reference as part of the type of the variable.
@@deanjohnson8233 I don't think you understood what I said. Of course it is part of the type whether something is a pointer or not. But in the declaration statement, the star is not part of the token that describes the type, which is proven by the example I supplied. Also, a defect? It would be incredibly easy to "fix" if it was. It's by design.
The unsafe examples are potentially useful for interop. Although generally you use IntPtr and Marshalling when possible. Also, fixed lets you use fixed-sized arrays without initializing one. It's very niche but it's the only thing I've actually used fixed for.
My first thought was interop, though I didn't know this word for it. Otherwise my second was creating your own little compress program. Taking all the variables into an array of pointers. Go through each pointer and look for the same char or int in the same spot. Put that char/int in it's own struct variable along with it's place in the pointer, and remove it from the original variables. Then to zip it up. Just run a function that takes the struct and puts the char/int into the coordinate place given. Can probably do that better using a dictionary but I just thought of a struct for simplicity. Probably a poorly optimized mess of a program that takes more memory than anything but I just liked to think up the concept of a zip compress program yourself by reducing the address values.
Unsefe, stackalloc, fixed and sizeof are good to know if you are using C# for Unity and you are using data-oriented technology stack. Stackalloc is good for avoiding false sharing.
There's also a project level property true which can also be set in the project properties (at least in Visual Studio), tab "build", then "Advanced", it's called "Check for arithmetic overflow". It sets the global default to "checked", then you can use the "unchecked" keyword to opt out. :-) From the language design view, they should have made "checked" the default, but nowadays, it's far too late to change the default, breaking 20 years of code. :-)
About the checked/unchecked keywords: unchecked is USUALLY unnecessary, because unchecked is the default state at runtime. However, CONSTANTS are checked at compile time by default, so if you want to cause an integer overflow with constants, the compiler will throw a compile time error at you and tell you to use the unchecked keyword. So for example, this: const int CONSTA = int.MaxValue; const int CONSTB = CONSTA + 42; will not compile. It will only compile if you write CONSTB = unchecked(CONSTA + 42);
The sizeof/unmanaged thing seems odd. When you pointed out that sizeof works with structs that didn't surprise me because internally, base types like int and byte ARE structs (except string which is an oddball type). "int" maps to struct Int32 {}... "long" maps to struct Int64{} and so on. Which now has me wondering why they would consider a struct to be "unmanaged". It does make a kind of sense in that structs are "value" objects not reference objects and have to have concrete existence when declared... but I'm not sure how that makes it "unmanaged".
Some other keywords which devs don't use often but should know how to use them: `volatile` for thread interaction, `extern` for defining PInvokes, and `implicit` and `explicit` for type conversion operators.
Being able to use unmanaged code can be extremely beneficial when you're dealing with optimisations and having to do a lot of calculations/large data sets. In addition, you should learn how to use weakrefs for much better io performance allowing you to use large amounts of memory better when you would otherwise have a lot of disk io. Server applications are really good for this. Things starts getting strange when you need to move in and out of managed space, like you need to have an unmanaged thread (like in a program written in CPP) suddenly enter managed space and then back out.
C# is a powerful and, *if you know what you're doing*, a potentially very fast language; but sometimes I worry about the size and inconsistency of the lexicon. We have changes in LINQ, introduction of C syntax, all of these things, which have worked out so far but tend to leave novices feeling out of their depth.
My first C# program in 2003 (the one where I 'cut my teeth' on C#, transitioning from C++) was an audio/MIDI app. Interacting with the WIN32 audio subsystem required allocating buffers, pinning them, and passing them via interop into unmanaged waveIn__ routines of the Winmm module. It was "unsafe" as heck, but what to expect from a C++ programmer :)
Thanks for sharing, I didn't know about a number of these. They help explain how to interact with unmanaged DLL's without having to resort to a C++/CLI layer, like I have in the days gone by. Thanks again!
One of the main reasons for using these is to write your own .NET bindings to unmanaged code - like P/Invoke to some DLL written in C/++ or even Rust. But nowadays there are tools that can generate that for free.
Real world example...I once had to index multiple millions of records of electoral role details on a laptop. The machine was disconnected and SSDs weren’t around at the time. To make the thing work in a realistic time it was an in memory solution. When I put everything in memory with a dictionary there simply wasn’t enough RAM for it to work. I ended up implementing a very lean hash table with collision detection using unsafe blocks and we managed to load everything in with room left over.
I do embedded systems programming and I can tell you I use all of those keywords with some frequency. Except stackalloc. That is a cool trick I'll have to remember.
It is all useful when it comes to create lowlevel graphic api wrappers etc btw "fixed" keyword can be used inside structs to create "embed" arrays inside struct if you really need cache friendly data with arrays in ECS or something
I've seen some unsafe code in graphics manipulation code. It may come into play if you need to access some types of hardware or something as well (if you're writing your own code and not using a library).
Some of these are less often used like checked/unchecked, but these are far from 'definitely never had to use' territory for me personally. It isn't even about performance either, just the constant need to have easy to use tools for PInvoking vendor libraries, system calls, etc. Some of these are still being improved like the stackalloc you mention wasn't possible to use in safe contexts until recently with the addition of Span. I'm glad Microsoft keep improving this space to be honest.
I thought you were going to include the goto statement, which I have only seen used once (not by me!) The last time I used goto was 40 years ago when I was doing Fortran!
I always use all the unsafe stuff. This is just the way to write real high performance code. And you are VERY underestimating the performance gain. In some cases it can make code hundreds or even thousands times faster. But yeah, this is not common approach for C#. Such things are really useful only when you are familiar with C/ASM level of things.
There is no doubt in my mind that coding like this would yield great performance gains but it comes with a lot of shortcomings. Code needs to be easy to maintain, easy to work with and easy to get new hires to work on. As a hiring manager myself, I can guarantee that if I was using all those keywords in our codebase then it would be impossible to hire engineers. And at the end of the day, it is way easier to scale an app out to 100 stateless instances than optimize that single codebase.
I was expecting to see likes of "__arglist", "__makeref", "__reftype" and "__refvalue" in these series, but all I saw were pretty normal everyday (or maybe, "every-month") keywords.
Resharper generates unchecked gethashcode by default. So that one I see often. Used all the unsafe features in my serialisation library (NHessian). Fastest way to calculate equality and hashcode for strings (char arrays) as well as decode Unicode.
I used checked/unsafe/fixed a lot back in.. gosh, .NET 1.1 days, when writing a scientific/image processing application. Obviously no stackalloc because it didn't exist back then, though I have used stackalloc in a small, high performance algorithm recently. Sizeof is the one I find most niche, despite it also being the one that most people would be familiar with. I know an int32 is going to be 32 bits == 4 bytes, so I don't need code to tell me that. But it's also kind of like using nameof(parameter) to get a string representation of a parameter or property or whatever - you don't need it _right now_ , but it makes maintaining it later a little easier.
It's checked at compiler time, so I sometimes have to do const int Default = unchecked(0xCA7F00D1); At runtime it is unchecked unless the compiler option CheckForOverflowUnderflow is set.
Cool. Actually we are using sometimes unsafe and stackalloc in our projects where we need really high performance (hot path). Also playing with structures to achive like super performance 0 allocation lock free hype stuff. =)
I'm trying to work out why they defailed to unchecked. Your example of the max value is good. The answer becomes wrong, I believe. Strange that you can't override to unchecked rather.
I've used all except stackalloc. But not often. Apart from unchecked, which is needed for good implementations of GetHashCode (among other things, but that's the common one).
I've used all of these but only because I have done some low level graphics stuff from C# for intrest rather than any real use but it an example of when you might do such a thing
I love the video but I feel like you were worried about people not understanding char* and -> when you haven't explained memory in the video. Those that understand memory don't need to worry about char*
It's hardly legacy code. Pointers are useful for interop and low level performant code. In dotnet5/C# 9 we've also now got function pointers. Stackalloc also returns a raw pointer by default, that's why he wrote "Span" instead of just "var".
@@igorthelight muhahah and now I build an undocumented nuget package and use botnets to download it constantly so its always on top of search results xD
DisplaySizeOf() method doesn't actually need to be marked as unsafe. Additionaly to the fixed keyword, there is GCHandle.Alloc(obj, GCHandleType.Pinned) in case you need to retain the pointer across multiple scopes and release it in completely another scope (like a Dispose() method or a finalizer). You can cheat and essentially bypass the need for the 'unsafe' keyword by using IntPtr and Marshal class for most of the pointer functionality (which is still unsafe).
Seems like many of these are low level enabling features which imo is better left to C or C++. C# is a great language but its never likely to get the same performance in these situations. If you want direct control of your memory you really should be using C++ or maybe rust? C# is meant to be high level and take care of low level operations for you at the expense of some performance impact. For most use cases C# is great and versatile and it can get closer to the hardware reasonably well, far better than many other high level languages e.g. python, java. StackAlloc seems to be the only one of these I would probably use in C#, for low level I would have to consider the overhead of calling into a C++ DLL vs just using these low level features.
Was messing around with silk . net a c# wrapper for stuff like opengl, vulkan, glfw and other common game development libraries. and had to get to try and understand unsafe, fixed and how pointers worked. it was very confusing at first, coming from .net mvc background. unfortunately I had to postpone the project indefinitely when covid happened, so I barely remember any of it anymore.
I remember using unsafe code to write a weird implementation of a voronoi noise bitmap generator using pointers. It was more of an exercise than it was of any actual use, and it was slow as all heck, but it was definitely fun to mess around with lower level stuff in C#. But hey it's called unsafe for a reason, right? (•~•)
Aaaaa. I mod native games using C# on a regular basis and I practically use those keywords daily. I was expecting something of the likes of volatile ahahaha.
I knew about all these but don't use them in my day to day coding. They history behind these keyword goes back to the very early versions of C# when C# didn't have a ton of libraries with managed code and you had to interact alot more with c++ libraries in Windows than you do now.
I use all of those very often, except stackalloc (since it can stack overflow) because it runs faster. I was expecting the 4 __ keywords to be on this video lol. I do not use the __ keywords often lol.
This part is usefull when you want to use some native dll functions you'll have to wrap up all those functions and "unsafe" & co will be your friends :)
Or couldn't you just use dll import? I've never had an issue using dll import to use my c++ functions. Barely ever used it but it works. Not sure on the limitations though
@@Ownage4lif31 right now it can be. Ten years ago when I made some wrappers over few Delphi libraries I had to do that. One of the libraries was a protocol over TCP/IP and I had some issues about how data types are stored. Honestly speaking I don't remember exactly what was the issue but when an c# app send the data to our delphi ecosystem apps it was received corrupted. Happy codding!
That's an interesting video, I've been programming in C# for nearly 15 years now and I didn't know about the checked keyword. I actually use "unsafe" quite a lot when I'm calling Windows APIs because they deal with pointers quite often but if you're doing that, you kind of have to use "fixed" or else, like you mentioned your next look at the pointer might not be valid. I also use sizeof when I'm writing out custom binary data files when I don't have access to a database.
On the subject of “stackalloc” is that at all similar to “malloc” from C? I haven’t looked too much into it, but if it means “memory allocate” (I.e. to RAM) and “stack allocate” for the prior- what does the other variation, “heapalloc” do?
"stackalloc" is like "malloca" from C I believe. And you have to be extremely careful with it since it will become deallocated the moment you return from the enclosing function and drop the stack frame. If you want data allocated on the stack, use a struct. That's what those are for.
Regarding "heapalloc", I believe that it just does what "malloc" would do in most cases. (And I believe it's native WIN32, so unmanaged) Evidentally you can also create multiple heaps, which I suspect is something more interesting for people writing the actual OS and maybe device drivers than ordinary programmers. I can imagine where from the OS's perspective, each process has its own private heap, but of course the OS has to make all that work.
@@nickchapsas Sorry, as you may have seen already, I've added a comment to one of your other videos as to why I specifically don't add underscores for this reason.
The first example is kind of wrong. When we add 1 to Max Uint (binary 32 1-s, or 0xFFFFFFFF) the result is 1 and 32 zeroes. Like adding 1 to 999 makes it one and 3 zeroes.. Since Uint is 32 bits long, 33 bits long record would be truncated. And one at the start would be cut and we'd end up with 32 zeroes. And that's ZERO. Checked keyword returned "overflow" meaning we've ended up with 33 bits long data that can be only 32 bits long... Like when you make a cupcake and in it oven it becomes bigger than the cup..
In case anyone wonders about the line using the -> operator:
pointer->ToString() is the same as (*pointer).ToString()
So, it means, "get the int from pointer and call ToString() on it"
and the parens around *pointer are needed due to operator precidence
This is old news for anyone with the smallest amount of experience in C or C++
more formally, it's not "getting an int", but rather dereferencing for you
@@pxolqopt3597 It's news to us who never touched C or C++(whose file type is called cpp) even after seeing C and C++ code several times.
"checked" is useful in some non-low-level code too, when converting from 64-bit IDs in a database to 32-bit IDs in an API.
Minor complaint about the the thumbnail. I'd love to be able to see these commands before I click on the video instead of them being blurred out, it'd peak my interest and give that effect of like "oh what does that keyword do"
@@lorinatzberger3624 Imo the clickbait would be more enticing if it wasn't blurred.
@@lorinatzberger3624 Fair point
i've had to use all but one of them, i've never used stackalloc, but since i'm working with C# to C++ interop, these concepts are actually used quite frequently :P
Damn I didn't know that people still do C# to C++ interop. That just blows my mind.
@@nickchapsas I use interop in mobile xamarin projects, we have some library implementing digital signature and encryption in GOST algorhytm, it is implementing Microsoft C++ CryptoApi, only way to use it on mobile is via interop. Only things I use for it is Marshal class and sequental layouted structs.
@@nickchapsas well i'm writing a C++ library for unity, so most of these things have at least cropped up at some point, though we do our best to limit it, since it does add a lot of complexity
@@nickchapsas try calling a Win32-API...
@@nickchapsas Then I'd recommend you look into the Stride Engine.
A really buggy and fragile mess but it's mostly 90 - 99% written in C# and some basic bare bones C++.
I've used sizeof a few times, it's useful when implementing low level integrations that require fixed package sizes. When simulating a TCP/IP protocol for instance.
In 'normal' or usual C# (C# only development) majority of these keywords probably wouldn't be used.
But if your C# program works with unmanaged code (C++, Fortran...) these keywords are pretty useful. For example sizeof is used for types interoperability.
You must be specially careful when mixing managed and unmanaged code.
Another great video, Nick!
Unsafe is C trying to fool us into using it
_takes your hand_
join me in the dark side
for real though C is addicting, you should try it some time
@@unsafecast3636 nah. Lack of normal build system and package manager kills any motivation to write anything more serious than helloworld's in it.
@@Xotchkass i think youre forgetting about cmake, conan & vcpkg
2:17 - casting an IntPtr to an int under 64-bit where that int would overflow will also throw an OverflowException, as the conversion occurs in a checked context. Using unchecked will also silence some overflow warnings, e.g. when working with the lparam in WndProc.
5:34 - that's a 64-bit pointer, so 6:39 you probably want to cast to a long, as casting to an integer will chop off the upper 32 bits of the 64-bit pointer.
9:16 - C# strings are not null terminated - the implementation just happens to put zeroes in the slack space after the end of the string.
In the C# language specification, section "22.7 The fixed statement" it says:
A char* value produced by fixing a string instance always points to a null-terminated string. Within a fixed statement that obtains a pointer p to a string instance s, the pointer values ranging from p to p + s.Length ‑ 1 represent addresses of the characters in the string, and the pointer value p + s.Length always points to a null character (the character with value '\0').
When reading the title, I definitely assumed at you would cover “volatile”. :)
Funny enough, volatile is in the part 2 of this video :D
Same thing that poped in my head lol
I don't think volatile is applicable to this topic. I completely agree about all that unsafe stuff, it is almost never needed even if you sometimes work with unmanaged code like me. But volatile is not about low level code, it is about thread-safety in high level code, many people use it because it is more convinient way than explicitly using Interlocked class.
@@iGexogen The topic isn't about low level code. It's about keywords that you don't usually see used in C#. I just happened to have grouped many low level stuff in one video to help it be easily consumed.
@@nickchapsas It will be interesting to see how many of commentators actually use volatile, I think that unity guys must be using advanced multi-threading. It was suprising for me that you never needed it, I suppose lock statements and concurrent collections is enough for all your async purposes. Waiting for part 2)
When doing image processing, unsafe is the best option for performance.
I want to point out something about pointers in C#:
in C#, the star is part of the type.
In C++ and C, the star is part of the declarator.
What does this mean?
In C#, "int* a, b;" will declare two int pointers.
In C++, "int* a, b;" will declare an int pointer and an int.
This is why we align stars and ampersands (for references, I'm not talking about the adress-of operator) to the right in C++. Because it is part of the declarator: "int *a, b; int *x, *y;"
So if someone puts the star on the left in C++, that doesn't mean they have different style taste, it just means they don't understand how it actually works.
Really solid explanation. Thank you.
I agree mostly, with the exception of your last remark. I use right-aligned operators too, but I also understand why people use left aligned operators. Especially in businesses where multiple declarations in a single line are forbidden in their coding standard (a lot of companies have this as a rule). I would argue that aligning them to the left can make it a lot more clear to the reader what a variable or member can contain, there is a lot of debate of semantics versus technicality here. Saying someone does not understand how it works because you do not agree with their choices, is not a constructive point. I have had people that designed compilers explain to me why they used left aligned.
@@BGroothedde I guess I was being a little too general here. But 90% of the time, people put it on the left because they don't understand the implications of it. Maybe not in a job setting, but when people make the decision themselves.
We can argue about good practice, but that never ends well.
My stance on style is just to do what the project you're joining already does, so if a company puts the star on the left, I'll do that too. But if you see a star on the left in a personal project, it's an _indication_ that they lack some understanding of the language.
But frankly, I was unaware that some companies do it, because it is imo just incorrect, the star belongs to the declarator. But if you don't ever group declarations (which I also don't do), then it might not matter so much.
Saying the star is not part of the type in c++ is nonsense. If that was the case, std::is_pointer and std::is_pointer should return the same value. Obviously they don’t and there are plenty of other examples where in type contexts whether something is a pointer type or not matters.
I get what you are saying about multiple variable declarations, but I would contend that is a defect of the parsing rules of the language.
In all other cases, you need to consider pointer/reference as part of the type of the variable.
@@deanjohnson8233 I don't think you understood what I said. Of course it is part of the type whether something is a pointer or not. But in the declaration statement, the star is not part of the token that describes the type, which is proven by the example I supplied.
Also, a defect? It would be incredibly easy to "fix" if it was. It's by design.
I am so glad that i found this channel. It always keeps my attention, and the concepts discussed are usually so intriguing
The unsafe examples are potentially useful for interop. Although generally you use IntPtr and Marshalling when possible.
Also, fixed lets you use fixed-sized arrays without initializing one. It's very niche but it's the only thing I've actually used fixed for.
And it can also be used with struct which I wanted to show but I totally forgot 🤦♂️
My first thought was interop, though I didn't know this word for it.
Otherwise my second was creating your own little compress program.
Taking all the variables into an array of pointers.
Go through each pointer and look for the same char or int in the same spot.
Put that char/int in it's own struct variable along with it's place in the pointer, and remove it from the original variables.
Then to zip it up. Just run a function that takes the struct and puts the char/int into the coordinate place given.
Can probably do that better using a dictionary but I just thought of a struct for simplicity.
Probably a poorly optimized mess of a program that takes more memory than anything but I just liked to think up the concept of a zip compress program yourself by reducing the address values.
We do image manipulations in unsafe mode for performance reasons. Best to do this in a seperate project.
Unsefe, stackalloc, fixed and sizeof are good to know if you are using C# for Unity and you are using data-oriented technology stack. Stackalloc is good for avoiding false sharing.
I actually learned about Unsafe code because of unity, Though I didn't know about stackalloc.
There's also a project level property true which can also be set in the project properties (at least in Visual Studio), tab "build", then "Advanced", it's called "Check for arithmetic overflow". It sets the global default to "checked", then you can use the "unchecked" keyword to opt out. :-)
From the language design view, they should have made "checked" the default, but nowadays, it's far too late to change the default, breaking 20 years of code. :-)
About the checked/unchecked keywords: unchecked is USUALLY unnecessary, because unchecked is the default state at runtime. However, CONSTANTS are checked at compile time by default, so if you want to cause an integer overflow with constants, the compiler will throw a compile time error at you and tell you to use the unchecked keyword. So for example, this:
const int CONSTA = int.MaxValue;
const int CONSTB = CONSTA + 42;
will not compile. It will only compile if you write CONSTB = unchecked(CONSTA + 42);
I love your videos, but your advanced c# stuff is extra awesome.
Great video.
This won't be weird if you are a C developer. Actually you had to deal with most of those keywords every day
All of these can be very handy when interfacing with C code (P/Invoke etc.) to reduce heap allocation in C# code when you know what you're doing..
The sizeof/unmanaged thing seems odd. When you pointed out that sizeof works with structs that didn't surprise me because internally, base types like int and byte ARE structs (except string which is an oddball type). "int" maps to struct Int32 {}... "long" maps to struct Int64{} and so on. Which now has me wondering why they would consider a struct to be "unmanaged". It does make a kind of sense in that structs are "value" objects not reference objects and have to have concrete existence when declared... but I'm not sure how that makes it "unmanaged".
The sizeof keyword is very useful when dealing with gpu calls. I do this a lot in unity using compute shaders and compute buffers
Some other keywords which devs don't use often but should know how to use them: `volatile` for thread interaction, `extern` for defining PInvokes, and `implicit` and `explicit` for type conversion operators.
I've used unsafe code in one of my projects. Used pointers to speed up bitmap calculations.
Being able to use unmanaged code can be extremely beneficial when you're dealing with optimisations and having to do a lot of calculations/large data sets. In addition, you should learn how to use weakrefs for much better io performance allowing you to use large amounts of memory better when you would otherwise have a lot of disk io. Server applications are really good for this.
Things starts getting strange when you need to move in and out of managed space, like you need to have an unmanaged thread (like in a program written in CPP) suddenly enter managed space and then back out.
C# is a powerful and, *if you know what you're doing*, a potentially very fast language; but sometimes I worry about the size and inconsistency of the lexicon. We have changes in LINQ, introduction of C syntax, all of these things, which have worked out so far but tend to leave novices feeling out of their depth.
My first C# program in 2003 (the one where I 'cut my teeth' on C#, transitioning from C++) was an audio/MIDI app. Interacting with the WIN32 audio subsystem required allocating buffers, pinning them, and passing them via interop into unmanaged waveIn__ routines of the Winmm module. It was "unsafe" as heck, but what to expect from a C++ programmer :)
I like to mod in games. and to know that you can do pointers natively in C# without a 3rd party dependency is great
Thanks for sharing, I didn't know about a number of these. They help explain how to interact with unmanaged DLL's without having to resort to a C++/CLI layer, like I have in the days gone by. Thanks again!
One of the main reasons for using these is to write your own .NET bindings to unmanaged code - like P/Invoke to some DLL written in C/++ or even Rust. But nowadays there are tools that can generate that for free.
A tool such as...? I don't need to do it often, but it's always a pain when I do.
Guess what those tools use under the hood. ;-)
I've used stackalloc and sizeof once; when generating waveform images of hours long audio files.
Real world example...I once had to index multiple millions of records of electoral role details on a laptop. The machine was disconnected and SSDs weren’t around at the time. To make the thing work in a realistic time it was an in memory solution. When I put everything in memory with a dictionary there simply wasn’t enough RAM for it to work. I ended up implementing a very lean hash table with collision detection using unsafe blocks and we managed to load everything in with room left over.
Wow, in C#?
I do embedded systems programming and I can tell you I use all of those keywords with some frequency.
Except stackalloc. That is a cool trick I'll have to remember.
As an experienced C programmer these are great. You have to worry about your own pointer safety, but Jesus is it running fast.
It is all useful when it comes to create lowlevel graphic api wrappers etc
btw "fixed" keyword can be used inside structs to create "embed" arrays inside struct if you really need cache friendly data with arrays in ECS or something
I've seen some unsafe code in graphics manipulation code. It may come into play if you need to access some types of hardware or something as well (if you're writing your own code and not using a library).
C sharp generally doesn't run in contexts that have access to memory mapped hardware.
That sizeof code example is pretty damn cool
Some of these are less often used like checked/unchecked, but these are far from 'definitely never had to use' territory for me personally. It isn't even about performance either, just the constant need to have easy to use tools for PInvoking vendor libraries, system calls, etc. Some of these are still being improved like the stackalloc you mention wasn't possible to use in safe contexts until recently with the addition of Span. I'm glad Microsoft keep improving this space to be honest.
I thought you were going to include the goto statement, which I have only seen used once (not by me!) The last time I used goto was 40 years ago when I was doing Fortran!
I always use all the unsafe stuff. This is just the way to write real high performance code. And you are VERY underestimating the performance gain. In some cases it can make code hundreds or even thousands times faster.
But yeah, this is not common approach for C#. Such things are really useful only when you are familiar with C/ASM level of things.
There is no doubt in my mind that coding like this would yield great performance gains but it comes with a lot of shortcomings. Code needs to be easy to maintain, easy to work with and easy to get new hires to work on. As a hiring manager myself, I can guarantee that if I was using all those keywords in our codebase then it would be impossible to hire engineers. And at the end of the day, it is way easier to scale an app out to 100 stateless instances than optimize that single codebase.
@@nickchapsas in general - yes. But sometimes ua-cam.com/video/7GTpwgsmHgU/v-deo.html
When you started typing i thought: Stop! I never had ro use uint before lol
I was expecting to see likes of "__arglist", "__makeref", "__reftype" and "__refvalue" in these series, but all I saw were pretty normal everyday (or maybe, "every-month") keywords.
Resharper generates unchecked gethashcode by default. So that one I see often.
Used all the unsafe features in my serialisation library (NHessian). Fastest way to calculate equality and hashcode for strings (char arrays) as well as decode Unicode.
I used checked/unsafe/fixed a lot back in.. gosh, .NET 1.1 days, when writing a scientific/image processing application. Obviously no stackalloc because it didn't exist back then, though I have used stackalloc in a small, high performance algorithm recently.
Sizeof is the one I find most niche, despite it also being the one that most people would be familiar with. I know an int32 is going to be 32 bits == 4 bytes, so I don't need code to tell me that. But it's also kind of like using nameof(parameter) to get a string representation of a parameter or property or whatever - you don't need it _right now_ , but it makes maintaining it later a little easier.
I'm quite suprised by checked/unchecked. I always thought checked was the default.
It's checked at compiler time, so I sometimes have to do
const int Default = unchecked(0xCA7F00D1);
At runtime it is unchecked unless the compiler option CheckForOverflowUnderflow is set.
Cool. Actually we are using sometimes unsafe and stackalloc in our projects where we need really high performance (hot path). Also playing with structures to achive like super performance 0 allocation lock free hype stuff. =)
You have such a great channel, I always learn something!
I'm trying to work out why they defailed to unchecked. Your example of the max value is good. The answer becomes wrong, I believe. Strange that you can't override to unchecked rather.
I've used all except stackalloc. But not often. Apart from unchecked, which is needed for good implementations of GetHashCode (among other things, but that's the common one).
Excellent video! I actually didn’t know about checked or stack_alloc.
I've used all of these but only because I have done some low level graphics stuff from C# for intrest rather than any real use but it an example of when you might do such a thing
I love the video but I feel like you were worried about people not understanding char* and -> when you haven't explained memory in the video. Those that understand memory don't need to worry about char*
I always just assumed c# had pointers :D Now that I know, I will have some fun with it :D Must leave unmanagable legacy code for generations to come
You!
Get back here!
:-)
It's hardly legacy code. Pointers are useful for interop and low level performant code. In dotnet5/C# 9 we've also now got function pointers.
Stackalloc also returns a raw pointer by default, that's why he wrote "Span" instead of just "var".
@@igorthelight muhahah and now I build an undocumented nuget package and use botnets to download it constantly so its always on top of search results xD
@@Terrados1337 Ha-ha :-)
DisplaySizeOf() method doesn't actually need to be marked as unsafe.
Additionaly to the fixed keyword, there is GCHandle.Alloc(obj, GCHandleType.Pinned) in case you need to retain the pointer across multiple scopes and release it in completely another scope (like a Dispose() method or a finalizer).
You can cheat and essentially bypass the need for the 'unsafe' keyword by using IntPtr and Marshal class for most of the pointer functionality (which is still unsafe).
Seems like many of these are low level enabling features which imo is better left to C or C++. C# is a great language but its never likely to get the same performance in these situations. If you want direct control of your memory you really should be using C++ or maybe rust? C# is meant to be high level and take care of low level operations for you at the expense of some performance impact. For most use cases C# is great and versatile and it can get closer to the hardware reasonably well, far better than many other high level languages e.g. python, java.
StackAlloc seems to be the only one of these I would probably use in C#, for low level I would have to consider the overhead of calling into a C++ DLL vs just using these low level features.
I was grindering some weed when you declared the snoopDog variable.... I felt so alluded... 🤣
I have had to use sizeof once but it's not exactly common in C#.
I'm surprised goto wasn't on the list.
What did you use it for?
The one time I’ve used unsafe and pointers was using writable bit maps for creating height maps.
I've used all of them.
Thanks for your videos! One of the best .net youtuber
Was messing around with silk . net a c# wrapper for stuff like opengl, vulkan, glfw and other common game development libraries. and had to get to try and understand unsafe, fixed and how pointers worked. it was very confusing at first, coming from .net mvc background. unfortunately I had to postpone the project indefinitely when covid happened, so I barely remember any of it anymore.
I litterally use all of these daily, currently working on 3D graphics software 😉
I know a little bit why you would use it. But then... WHY?
unsafe, sizeof, stackalloc are actually very useful for high-performance apps.
I actually have a use for the combination of unsafe, unmanaged and sizeof combo in a project I'm doing, lol.
Without watching the video, here are three my guesses:
Global
Goto
Unchecked
Lol i only got one right 😅
You technically got Global right as well. It will be in part 2 :D
I remember using unsafe code to write a weird implementation of a voronoi noise bitmap generator using pointers. It was more of an exercise than it was of any actual use, and it was slow as all heck, but it was definitely fun to mess around with lower level stuff in C#. But hey it's called unsafe for a reason, right? (•~•)
Why doesn't the compiler just make every "new" you use in a local scope into stackalloc?
For me it's checked and unchecked are new keywords. But others are good to revise 😁
Back in .NET 2, 3, 3.5 and 4 i used pointers all the time, because those versions were sooooo slooooow.
already used for years, except checked
you never need class keyword you can trust me.
i always imagined sizeof is pretty universal across langs
Some other keywords: __arglist, __makeref, __refvalue, __reftype
Aaaaa.
I mod native games using C# on a regular basis and I practically use those keywords daily.
I was expecting something of the likes of volatile ahahaha.
No mention of "dynamic" in this and the sequel it's just that variable type is determined at runtime
Dynamic is very common still, mainly due to packages like Dapper
Use more often the "SnoopDog"-pattern :-)
420
From now on I will always call my unsafe pointers snoopDog
I knew about all these but don't use them in my day to day coding. They history behind these keyword goes back to the very early versions of C# when C# didn't have a ton of libraries with managed code and you had to interact alot more with c++ libraries in Windows than you do now.
I did not know that, very interesting🤔
I guess fixed could be used if you were to manage password or encription
I use all of those very often, except stackalloc (since it can stack overflow) because it runs faster. I was expecting the 4 __ keywords to be on this video lol. I do not use the __ keywords often lol.
This part is usefull when you want to use some native dll functions you'll have to wrap up all those functions and "unsafe" & co will be your friends :)
Or couldn't you just use dll import?
I've never had an issue using dll import to use my c++ functions. Barely ever used it but it works. Not sure on the limitations though
@@Ownage4lif31 right now it can be. Ten years ago when I made some wrappers over few Delphi libraries I had to do that. One of the libraries was a protocol over TCP/IP and I had some issues about how data types are stored. Honestly speaking I don't remember exactly what was the issue but when an c# app send the data to our delphi ecosystem apps it was received corrupted.
Happy codding!
I have used them all, and then some :)
You could also include the 'sbyte' keyword. Like, literally, why would anybody want to use it?
@@alexanderboll235 The docs recommend Int16 instead. Which only uses twice as much memory...
It's a CLS compliance thing. docs.microsoft.com/en-us/dotnet/api/system.sbyte?view=net-5.0
Not sure why it's not CLS compliant but there you go
You use all of these a lot when doing graphics programming with opengl or similar
Yeah apparently graphics people are using those a ton. I had no idea and I’m so pleasantly surprised
Why are you using uint? Do you actually c#?
I think restriction of sizeof is not unmanaged, it's should be value type and not reference type!
It is value type
I didn’t know c# had pointers capability. And the unsafe syntax is the same as c++ syntax
That's an interesting video, I've been programming in C# for nearly 15 years now and I didn't know about the checked keyword. I actually use "unsafe" quite a lot when I'm calling Windows APIs because they deal with pointers quite often but if you're doing that, you kind of have to use "fixed" or else, like you mentioned your next look at the pointer might not be valid. I also use sizeof when I'm writing out custom binary data files when I don't have access to a database.
On the subject of “stackalloc” is that at all similar to “malloc” from C? I haven’t looked too much into it, but if it means “memory allocate” (I.e. to RAM) and “stack allocate” for the prior- what does the other variation, “heapalloc” do?
malloc in C allocates on the heap. Stackalloc is like c arrays. E.g
signed int numbers[3] = {4,2,0};
`malloc` in C allocates memory on the heap, not on the stack.
"stackalloc" is like "malloca" from C I believe. And you have to be extremely careful with it since it will become deallocated the moment you return from the enclosing function and drop the stack frame.
If you want data allocated on the stack, use a struct. That's what those are for.
Regarding "heapalloc", I believe that it just does what "malloc" would do in most cases. (And I believe it's native WIN32, so unmanaged)
Evidentally you can also create multiple heaps, which I suspect is something more interesting for people writing the actual OS and maybe device drivers than ordinary programmers. I can imagine where from the OS's perspective, each process has its own private heap, but of course the OS has to make all that work.
The one keyword you absolutely never have to use is 'ascending'. It is totally 100% superfluous.
Why do you prefix member variables with an underscore?
To distinct between local variables and class level variables
@@nickchapsas Sorry, as you may have seen already, I've added a comment to one of your other videos as to why I specifically don't add underscores for this reason.
@@MrMatthewLayton That's cool, people don't have to use the same convensions
The first example is kind of wrong.
When we add 1 to Max Uint (binary 32 1-s, or 0xFFFFFFFF) the result is 1 and 32 zeroes. Like adding 1 to 999 makes it one and 3 zeroes.. Since Uint is 32 bits long, 33 bits long record would be truncated. And one at the start would be cut and we'd end up with 32 zeroes. And that's ZERO.
Checked keyword returned "overflow" meaning we've ended up with 33 bits long data that can be only 32 bits long... Like when you make a cupcake and in it oven it becomes bigger than the cup..
great vid, mate!
unsafe is used sometimes when you do renders
You definitely used sizeof if you ever dealt with any Windows API with Interops and Marshaling via P/Invoke, pinvoke website is life
Need units on sizes IMO...
What editor is he using? Debugging looks nice.
I'm using JetBrains Rider
You forgot the "in" parameter prefix keyword. :)
Bigger font sizes, please... this is hard on mobile
Nice, thanks so much!)))