If you really wanted to, you can program in a subset of C# that is similar to C. You can avoid the garbage collector by using a native memory allocator and then manipulate pointers with the usual * and -> operators.<p>While you probably don't want to write a whole program like that, sometimes it is helpful to use these features when porting code from C or for performance reasons. See for example this implementation of memmove that is used for copying strings and other things:<p><a href="https://github.com/dotnet/coreclr/blob/7ddd038a33977b152e856448443425cbc6b591c6/src/System.Private.CoreLib/src/System/Buffer.cs#L183" rel="nofollow">https://github.com/dotnet/coreclr/blob/7ddd038a33977b152e856...</a>
For those saying a GC is too high level, malloc is also a high level abstraction. There are relatively simple implementations of malloc, but allocators like jemalloc are starting to look quite a lot like a GC in the ways they manage memory.<p>The real issue with GCs is the lack of control that makes it very difficult if not impossible to do things like grouping allocations together. However, this capability comes at a cost since then you can’t move around existing allocations without going to extreme lengths and can easily lead to memory fragmentation.
This conflates the language, a particular implementation of that language, and a particular runtime that said implementation uses.<p>As far as the language goes, you can do anything in C# that you can do in C in terms of memory-unsafe programming - raw pointers, pointer arithmetic, unions, allocating data on the stack - it's all there, and it's not any harder to use. Since semantics are the same, it should be possible to implement it just as efficiently as in C. At that point, the only overhead that you can't remove by avoiding some language feature or another is GC, although you can avoid making any allocations through it. So the real question is whether a language with GC can be considered low-level.
C# is managed code, so no, it’s not low-level by any reasonable definition of the term and calling it so would be confusing to anyone getting started in this stuff.
Unity is also going to do some C++ -> C# conversions
<a href="https://blogs.unity3d.com/2019/02/26/on-dots-c-c/" rel="nofollow">https://blogs.unity3d.com/2019/02/26/on-dots-c-c/</a>
I remember back when they called C a high level language- because it wasn’t assembler. Yes, I’m old. But, my point is that there probably isn’t a definitive answer to that question that won’t change with changing technology.
This has a click-baity title. A mostly math/calculation heavy C++ program is translated into a mostly math/calculation heavy C# program. You could replace either side with any language and get the same outcome. At best, the end of the article lists some hacks you _could_ write in C# to get what you consider "low-level", but ultimately defeat the purpose of using a "high-level" language in the first place. If you're using unsafe calls and platform intrinsics, it's more than a one off situation, and the language doesn't encourage it, then maybe you're trying to hammer a nail with a shoe.
Slightly to the side of the topic, but this quote here:<p>>Yep, Bob uses i++ instead of ++i everywhere. Meh, what’s the difference.<p>I know what the difference is, but if you're just using it on its own line or in a for loop, why would you use `++i` instead of `i++`?
As soon as you default to a particular policy of how to allocate, and most importantly free, memory on the heap, you have committed yourself. Low level means: no commitment in that realm, aka, do what you please. The fact that you can possibly overrule that default, does not make much of a difference, because almost any language allows this.<p>C does not even have a standard (dynamically-sized) list concept built in, because that would amount to committing oneself to a default heap allocation/deallocation policy. All you can get is a contiguous range of bytes with some vague, default interpretation as to what it is supposed to be, through the use of malloc/free (possibly hacking it through realloc). That is why C is considered low level.<p>Still, in a true low-level solution, you would use the sbrk system call directly. So, in a sense, C may already "add too much value" to be considered truly low level.
It's subjective but recent language and runtime updates have made massive updates in performance and flexibility. There are projects like Unity, RavenDB document store, FASTER KV store, Trill analytics, ML.NET and many more that show that C# is incredibly capable of competing at low-level/high-performance scenarios.