Alternative Title, "6 quick ways to get your repo access revoked" Some examples, I can see some use cases, but most (specially the date) would likely get me shouted at. Heard you like some async, so I put some more async inside your async.
For those who are wondering: in `async async async(async async) => await async`, we have: * First async: the keyword. * Second async: the method return type. * Third async: the method name. * Forth async: the parameter type. * Fifth async: the parameter name. * Sixth async: the parameter again.
The main point for me is that the async keyword was introduced in C# version 5, and in order not to break compatibility with older code, the compiler team probably decided that they couldn't just make such variable or parameter names suddenly forbidden. So they really had to work around it and support it both as a identifiers as well as keywords, depending on the place where they are used. However, on the other hand, I am not sure at this point why I cannot make a variable called await, but while I can call it async.
@@jongeduard You can make a variable named "await", just not inside an async method. Because 'await' has a special meaning in that context. On the other hand, 'async' only has a special meaning as a modifier in the method declaration.
@@louisfrancisco2171 Thanks! That explains it all, and also confirms the compatibility thing even more! However, what must have confused me, when I quickly test on Sharplab, I get a compiler error for it in top level code too (no problem since the whole top level code thing far newer). When I make a synchronous local function inside that, the variable name is accepted. LOL. Thinking further that actually makes sense too, since top level code is optionally asynchronous too, as soon as the first await call is written.
2:07 I would have gone one bit further and add an implicit conversion to string. 8:50 "Be very careful with your extension methods on types you don't own" There is usually less need to add extension methods on types you do own. I tried some crazy stuff, just for the fun of it. For example: WriteLine(from x in 42 where 7 select x); This writes "[0, 7, 14, 21, 28, 35, 42]"
The new insane thing to do in c#8 is using static methods in interfaces. It's powerfull as hell. There's loads of interfaces already existing. For example, if you want a type to be parsable from string, just derive it from IParsable. You know that particular type will have Parse and TryParse methods. If you create a generic class/method like this : public void TestParse(string value) where T : IParsable you'll then be able to write : T v = T.Parse(value); This comes with many more interfaces such as IAdditionOperators, ISubstractionOperators, ITrigonomertyFunctions... This means you can define you own class with compiled standardized names. That's so cool ! This also means that you no longer have to create functions for each numeric type; you can handle everything at once : public T Addition(T num1, T num2) where T : IAdditionOperators => num1 + num2; for example, here's the signature for my matrix class : public sealed partial class Matrix : IFormattable, IEquatable, IEquatable, IEquatable, IEquatable, ICloneable, IAdditionOperators, ISubtractionOperators, IEqualityOperators, IEqualityOperators, IMultiplyOperators, IMultiplyOperators, IMultiplyOperators, IDivisionOperators, IUnaryNegationOperators, IUnaryPlusOperators where T : struct, IFloatingPoint, IPowerFunctions, ITrigonometricFunctions, IRootFunctions It now handles both double and float
Just remember that you can get the length of the string directly from memory (unsafe obviously): TypedReference ref = __makeref(str); IntPtr ip = **(IntPtr**) &ref; var ptr = (int*) ip.ToPointer(); var len = *(ptr+2);
For the lock-monitor insanity my guess is that the lock is a syntax sugar which translates to the usage of System.Threading.Monitor and since you just created your own version of it it takes precedence over the BCL when compiling.
My first thought was for debugging concurrency issues. One thing I've done fairly recently was create an IDisposable class that takes a lock on an object and releases it upon disposal, but also allowing parameterizing a timeout before the lock fails, as well as logging a message if the lock is held for too long - and I could just replace the lock(obj) with a using(...). This class ended up helping a lot with some very improperly-threaded legacy code. If I altered the Monitor class like this I might have been able to get the diagnostics I needed much earlier.
One interesting that got junior devs stumped in my project was the use of implicit and explicit and how the hell incompatible types get assigned and yet compiled!
6:13 - `foreach (var i in 5..10)` - actually seems sensible and would be nice to have in the language, I think. Immediately, I went to check if `foreach (int i in [5,6,7,8,9,10])` would work, but unfortunately, it complains that there is no target type...
Well, in my experience the bullshit I did with C# I can name 2 things: extension methods for Span/ReadOnlySpan and overloading operator+ that accepts 2 different types and returns an enumerable of 3rd type. Something like Papa+Mama=Kids.
Bizarre operators usage in C#* *Me*: Nuts! *Also me trying to do anything in F#*: Yeah, ok, first things first - let's wrap this two-arguments function with some bizarre operator.
Something which fits nicely to this theme is Add() method duck-typing and collection initialization. So you could have something like new List { "2023-12-05" }; given that you have an extension method: public static void Add(this List col, string param) { col.Add(DateTime.Parse(param)); }
I saw most of these on a reddit post recently. Guessing you got the idea from the same post? The third one (range extensions) I've implemented myself and have been using for a few years, it's a shame it's not part of the default Range class/syntax.
@@nickchapsas Ah, maybe these must have been going round the internet for a while then. Either way, love your explanations on them and thanks for sharing!
the Monitor lock thing I could see maybe for debugging where you could log out which thread got the lock. But Heisenberg would rear his ugly head and the logging would change the timing of whatever thing you were trying to debug .
You say "Dont do this" but I have to say these are nice. As long as the extensions aren't GLOBAL, and only used with specific "using namespace", I see it as no different than other extension methods.
The first one is a thing in the C++ standard library. The filesystem::path class has / overloaded to append any string or path-like object. It feels wrong in a way, but actually quite ergonomic, and nice to know that path separators are all taken care of properly
Hehe, glad I shared Jared's async madness on Reddit the other day. It also highlights why the language design team are so careful when working with contextual keywords, for example in relation to the proposed "semi-auto properties" (exposing the implicit backing field of properties the "field" contextual keyword), in situations where you might already have a field named "field".
async can probably be used as a type or identifier because it was added later to C#, when there was already code that used that as a name. So in some contexts, the keyword can be used as an identifier. Same story with nameof.
I've used the integer awaiting before in a serious project, one that required lots of intricate precise timing. But instead of making custom awaiters on integers directly, I created a new class that was covariant with integer, and awaited that. (with the minor abstraction of 'beat', as this was timing against music, and so the length of time represented by a beat scaled with a 'tempo' setting) This and a simple static method to return beat let me 'await Beats(2)'.
But in that case, if you care about performance, I would have chosen to wrap it inside a struct and definitely not a class. There is no reason to allocate memory on the heap for 1 single integer value. Wrapping and int in a class instead of a struct is basically another way of boxing, al be it not in the popular terms.
@@jongeduard Absolutely true in general practice. What I am describing has both a small instance size, wraps a single value, and is immutable - all indicators towards a struct as the preferred approach. This particular case, however, had one reason why I went class over struct: those beats get passed around a lot. And I mean a lot. Since structs are value types, both referencing and any casting that happens as they get passed around and used generates more boxing and unboxing than using a struct saves. As a class though, just needed to pass the references around and saved that overhead. It also helps that I owned the only hardware that ever ran on, so knew exactly what the limitations were and didn't have to fine tune performance beyond what was needed to run the show - this was a live procedural music generator/lighting controller, that was generating both individual notes of the music and controlling individual movements/hue changes for the gobos.
@@joshpatton757 A struct containing an Int32 is 4 just bytes, the size of the int. While A reference is actually a pointer and is 8 bytes on a 64bit system. A class containing an Int32 needs both 8 bytes for each reference, as well as 4 bytes for the actual int value. When you have a struct that is no larger than 16 bytes, you are always the most efficient with a struct, both in CPU performance as well as in memory, even if you are copying it a million times. And that is exactly what Spans are as well, they are ref structs that contain both a pointer and a length value. And my personal benchmarks show even 24 byte structs to still be pretty memory efficient, I yet have to find out why exactly. Larger structs are indeed less memory efficient than reference types, but they often still win in CPU performance over reference types most of the time. This is purely because of GC overhead, always slowing down things. For each newly created reference to an object, the GC is doing extra work to track it. If you really need to pass references because you actually need to share the same thing between methods, the fastest thing is to use ref parameters to your struct. Then you don't have the copy problem at all, giving you both CPU and memory efficiency back.
ref means passing the reference of a value type, for example when you have an int as parameter, normally we pass a copy of the int to it, even the mothed edits the int value, it wound affect the original variable we passed, and if you allow the method to change it, pass (ref i) to it, in this case the method body has the reference to that int variable, any change on the int variable will affect the value itself
6:00 This trick is cooler with ranges, i.e. 0..5 It's also the only one I'd consider useful... strictly with ranges only... Well, maybe with a ValueTuple variant for setting the step size...
no, add an extension method Step for it! public static RangeWithStep Step(this Range rng, int step) => new(rng, step); foreach (var i in 0..5.Step(2)) { ... }
I used the Monitor technique to replace System.Net.Quic with OpenSSL and QUIC Datagram support on Windows 10 back in .Net 6 when .Net 7 was in preview and only supported Windows 11 with TLS 1.3 in SChannel. Used with conditional preprocessor to target different versions of runtimes.
The date and path joining tricks might come from someone trying to replicate the C++ standard library, those features were standardized relatively recently.
1. This technique is extremely useful in any language because it allows you to join values together in a clearly regulated way that's still easy to read. Use it where it clearly would make sense to, if you start overriding != to produce a class you really should be beat upside the head 6. Dear God, its type of crap that makes the most vexing parse sound trivial.
With the new "Extension Everything" feature coming in C# 13 we will be able to add the "/" operator directly to string, enabling this path concatenation hack without a helper class.
I remember working on PL/1 on the mainframe (I love that language with fond memories) but that 'async' example reminded me of something that broke my heart that you could do with PL/1... IF IFIF = IF THEN IFIFIF = IFIFIF; something to do with being able to use keywords when you probably shouldn't, it's just mischievous eh!
I like range extension to facilitate in foreach (not for integers, though), in fact, I don't quite understand why language don't have it by default - I mean, shouldn't ranges be iterable? Path joining - I think this a (rare) valid use case for operator overloading - I see no harm here. Rest is... well... no comment, lol
@03:04: a Microsoft manager probably won't showcase Jetbrains products, and only Jetbrains ReSharper / Rider can do certain refactorings. So I have doubts about that course's completeness (in terms of how to do them automatically).
duck typing in c# is extremely powerful, imagine what you can do if the switch operator/expression were able to resolve which "case" go into, by calling a special method (or a method on an interface, why not) on the type that need to be switched on....i hope they will do it one day.
I like the idea of piping file paths and I'd never thought about it. Is the only counterpoint that other developers will be confused by it? I'm tempted to keep using it.
Could I maybe use that monitor class override to use default timeout to lock keyword? So that all my locks would timeout, instead of wait indefinitely in case of deadlock? I am now using IDisposable LockWithTimeout(this object lck) ... method and "using" keyword to do the locking, but this would be much, much pretier.
When you were beginning to show the lock statement, I was thinking "will we try to goto into a locked lock statement maybe?". It didn't turn out that way, but I'm curious if it's possible. 🤔 (So can goto be used to enter a lock)
Wasn't the raw object locking as a mutex the old canonical way of locking stuff in C#? If I remember correctly it was because having a separate object as the mutex instead of locking the container itself is cleaner and less error prone supposedly. I could see that make sense in wrapper containers that maintain multiple internal representations for example but I never really understood the argument more generally tbh.
Insane C# techniques that no one in their right mind will use - except course the C# team, who will probably add them to the base language in the next release.
@@volan4ik. I know - just joking that many recent C# versions have come with new language features that modify the look of the language quite drastically (mostly for the better, it is to be said). It's good though - it can be hard to justify adopting approaches that are unconventional, if beneficial, when they are just some random NuGet library; when they are implemented within the language itself there's not so much second guessing using them when they are useful.
@asedtf I know of one niche thing, hooking functions used by the GC such as GlobalMemoryStatusEx. Managed code does not like being run in that context and will throw. So there are things.
Mine is just evil, not proud. Create a Console class with WriteLine method that prints "Compilation error" in a project of a newbie that didn't know about namespaces.
I cannot wait until async becomes a normal keyword and the async bs is no longer valid. Same goes for var, and many other contextual keywords that should no longer be accepted as valid identifiers.
For the "extension nonsense" example, I got an even better approach: var today= 30 / November / 2023; Just define a helper struct with static readonly members for each month, overriding "/" operator accordingly. And also put a "global using static" for that struct, so the month names can be unqualified 🤣🤣🤣
Enumerating a range of integers that way (a..b) actually sounds pretty neat. But I would need built-in support from the language out of the box, doing it custom like this is the smelliest of smells.
Alternative Title, "6 quick ways to get your repo access revoked"
Some examples, I can see some use cases, but most (specially the date) would likely get me shouted at.
Heard you like some async, so I put some more async inside your async.
But that's not a click bait title 😉
@@mikaelsyskaIts Nick, I'd still click it.
@@local9 yes, no matter what the title is, I would still watch.
For those who are wondering: in `async async async(async async) => await async`, we have:
* First async: the keyword.
* Second async: the method return type.
* Third async: the method name.
* Forth async: the parameter type.
* Fifth async: the parameter name.
* Sixth async: the parameter again.
That's obvious, but why c# allows method name and parameter's to be async? That's the question
Async is a contextual keyword, for backwards compatibility reasons. Same goes for var, await, get and set and many others
The main point for me is that the async keyword was introduced in C# version 5, and in order not to break compatibility with older code, the compiler team probably decided that they couldn't just make such variable or parameter names suddenly forbidden.
So they really had to work around it and support it both as a identifiers as well as keywords, depending on the place where they are used.
However, on the other hand, I am not sure at this point why I cannot make a variable called await, but while I can call it async.
@@jongeduard You can make a variable named "await", just not inside an async method. Because 'await' has a special meaning in that context. On the other hand, 'async' only has a special meaning as a modifier in the method declaration.
@@louisfrancisco2171 Thanks! That explains it all, and also confirms the compatibility thing even more! However, what must have confused me, when I quickly test on Sharplab, I get a compiler error for it in top level code too (no problem since the whole top level code thing far newer).
When I make a synchronous local function inside that, the variable name is accepted. LOL.
Thinking further that actually makes sense too, since top level code is optionally asynchronous too, as soon as the first await call is written.
2:07 I would have gone one bit further and add an implicit conversion to string.
8:50 "Be very careful with your extension methods on types you don't own" There is usually less need to add extension methods on types you do own.
I tried some crazy stuff, just for the fun of it. For example:
WriteLine(from x in 42 where 7 select x);
This writes "[0, 7, 14, 21, 28, 35, 42]"
The new insane thing to do in c#8 is using static methods in interfaces.
It's powerfull as hell.
There's loads of interfaces already existing. For example, if you want a type to be parsable from string, just derive it from IParsable. You know that particular type will have Parse and TryParse methods. If you create a generic class/method like this :
public void TestParse(string value) where T : IParsable
you'll then be able to write :
T v = T.Parse(value);
This comes with many more interfaces such as IAdditionOperators, ISubstractionOperators, ITrigonomertyFunctions...
This means you can define you own class with compiled standardized names. That's so cool !
This also means that you no longer have to create functions for each numeric type; you can handle everything at once :
public T Addition(T num1, T num2) where T : IAdditionOperators => num1 + num2;
for example, here's the signature for my matrix class :
public sealed partial class Matrix : IFormattable, IEquatable, IEquatable, IEquatable, IEquatable, ICloneable,
IAdditionOperators,
ISubtractionOperators,
IEqualityOperators,
IEqualityOperators,
IMultiplyOperators,
IMultiplyOperators,
IMultiplyOperators,
IDivisionOperators,
IUnaryNegationOperators,
IUnaryPlusOperators
where T : struct, IFloatingPoint, IPowerFunctions, ITrigonometricFunctions, IRootFunctions
It now handles both double and float
But that's C# 11 and dotnet 7, not C# 8.
@@jongeduard You are right, but i did not get it work properly in c#11
The first one is a technique Nuke Build uses for their paths. It also has implicit operator to cast it to string.
When I saw it in Nuke I thought it is so cool usage of operator overload
If I’m not mistaken, this technique was added to standard library in Python
@@Grafsnikers Its better part of the C++ standard since 2017 when std::filesystem::path came in C++17
Just remember that you can get the length of the string directly from memory (unsafe obviously):
TypedReference ref = __makeref(str);
IntPtr ip = **(IntPtr**) &ref;
var ptr = (int*) ip.ToPointer();
var len = *(ptr+2);
For the lock-monitor insanity my guess is that the lock is a syntax sugar which translates to the usage of System.Threading.Monitor and since you just created your own version of it it takes precedence over the BCL when compiling.
My first thought was for debugging concurrency issues.
One thing I've done fairly recently was create an IDisposable class that takes a lock on an object and releases it upon disposal, but also allowing parameterizing a timeout before the lock fails, as well as logging a message if the lock is held for too long - and I could just replace the lock(obj) with a using(...). This class ended up helping a lot with some very improperly-threaded legacy code. If I altered the Monitor class like this I might have been able to get the diagnostics I needed much earlier.
Thanks!
One interesting that got junior devs stumped in my project was the use of implicit and explicit and how the hell incompatible types get assigned and yet compiled!
6:13 - `foreach (var i in 5..10)` - actually seems sensible and would be nice to have in the language, I think.
Immediately, I went to check if `foreach (int i in [5,6,7,8,9,10])` would work, but unfortunately, it complains that there is no target type...
I think a lot of languages have this feature. Rust has it so you can do `for i in 0..5 { }`. It is so convenient.
I thought it looked like something from Python or the like
You are just missing a 'new' before those square brackets.
Well, in my experience the bullshit I did with C# I can name 2 things: extension methods for Span/ReadOnlySpan and overloading operator+ that accepts 2 different types and returns an enumerable of 3rd type. Something like Papa+Mama=Kids.
Bizarre operators usage in C#*
*Me*: Nuts!
*Also me trying to do anything in F#*: Yeah, ok, first things first - let's wrap this two-arguments function with some bizarre operator.
Something which fits nicely to this theme is Add() method duck-typing and collection initialization. So you could have something like new List { "2023-12-05" }; given that you have an extension method: public static void Add(this List col, string param) { col.Add(DateTime.Parse(param)); }
I saw most of these on a reddit post recently. Guessing you got the idea from the same post? The third one (range extensions) I've implemented myself and have been using for a few years, it's a shame it's not part of the default Range class/syntax.
I had shown 3/6 of them already in other videos in the past, one was from twitter which i mentioned and one was from a talk from Jared Parsons
@@nickchapsas Ah, maybe these must have been going round the internet for a while then. Either way, love your explanations on them and thanks for sharing!
Just odd timing as I thought I had deja-vu lol
This is one of my favorite videos as of late. "How the hell can we do this?" Always a winner. :)
the Monitor lock thing I could see maybe for debugging where you could log out which thread got the lock. But Heisenberg would rear his ugly head and the logging would change the timing of whatever thing you were trying to debug .
You say "Dont do this" but I have to say these are nice.
As long as the extensions aren't GLOBAL, and only used with specific "using namespace", I see it as no different than other extension methods.
For the async one, I wonder if that's from then they first introduced the keyword... so it wouldn't break existing code?
The overloading of `operator /` done in `FilePath` seems quite normal if you're familiar with C++ and `std::filesystem::path`.
That fileinfo operator is exactly how python's recommended pathlib.Path object works.
The first one is a thing in the C++ standard library. The filesystem::path class has / overloaded to append any string or path-like object. It feels wrong in a way, but actually quite ergonomic, and nice to know that path separators are all taken care of properly
that's better than using bit shift operators for printing to streams and reading from streams but it's in the same ballpark
@@Kitulous Yeah, the shift operators were... A Decision. I pretty much exclusively use superior third party libraries for those things lol
We did use the Extensions on int to create dates for our unit test.
IT IS really nice to read in tests that require different dates and such
Hehe, glad I shared Jared's async madness on Reddit the other day.
It also highlights why the language design team are so careful when working with contextual keywords, for example in relation to the proposed "semi-auto properties" (exposing the implicit backing field of properties the "field" contextual keyword), in situations where you might already have a field named "field".
async can probably be used as a type or identifier because it was added later to C#, when there was already code that used that as a name. So in some contexts, the keyword can be used as an identifier. Same story with nameof.
11:37 Filename checks out
I've used the integer awaiting before in a serious project, one that required lots of intricate precise timing. But instead of making custom awaiters on integers directly, I created a new class that was covariant with integer, and awaited that. (with the minor abstraction of 'beat', as this was timing against music, and so the length of time represented by a beat scaled with a 'tempo' setting) This and a simple static method to return beat let me 'await Beats(2)'.
But in that case, if you care about performance, I would have chosen to wrap it inside a struct and definitely not a class. There is no reason to allocate memory on the heap for 1 single integer value. Wrapping and int in a class instead of a struct is basically another way of boxing, al be it not in the popular terms.
@@jongeduard Absolutely true in general practice. What I am describing has both a small instance size, wraps a single value, and is immutable - all indicators towards a struct as the preferred approach.
This particular case, however, had one reason why I went class over struct: those beats get passed around a lot. And I mean a lot. Since structs are value types, both referencing and any casting that happens as they get passed around and used generates more boxing and unboxing than using a struct saves. As a class though, just needed to pass the references around and saved that overhead.
It also helps that I owned the only hardware that ever ran on, so knew exactly what the limitations were and didn't have to fine tune performance beyond what was needed to run the show - this was a live procedural music generator/lighting controller, that was generating both individual notes of the music and controlling individual movements/hue changes for the gobos.
@@joshpatton757 A struct containing an Int32 is 4 just bytes, the size of the int. While A reference is actually a pointer and is 8 bytes on a 64bit system.
A class containing an Int32 needs both 8 bytes for each reference, as well as 4 bytes for the actual int value.
When you have a struct that is no larger than 16 bytes, you are always the most efficient with a struct, both in CPU performance as well as in memory, even if you are copying it a million times. And that is exactly what Spans are as well, they are ref structs that contain both a pointer and a length value.
And my personal benchmarks show even 24 byte structs to still be pretty memory efficient, I yet have to find out why exactly.
Larger structs are indeed less memory efficient than reference types, but they often still win in CPU performance over reference types most of the time.
This is purely because of GC overhead, always slowing down things. For each newly created reference to an object, the GC is doing extra work to track it.
If you really need to pass references because you actually need to share the same thing between methods, the fastest thing is to use ref parameters to your struct. Then you don't have the copy problem at all, giving you both CPU and memory efficiency back.
but do not await Beasts(7);
Can you please do a video (if you haven't already) on all the uses and contexts of the "ref" keyword? I know one of them, but not the rest. Thank you!
ref means passing the reference of a value type, for example when you have an int as parameter, normally we pass a copy of the int to it, even the mothed edits the int value, it wound affect the original variable we passed, and if you allow the method to change it, pass (ref i) to it, in this case the method body has the reference to that int variable, any change on the int variable will affect the value itself
6:00 This trick is cooler with ranges, i.e. 0..5
It's also the only one I'd consider useful... strictly with ranges only...
Well, maybe with a ValueTuple variant for setting the step size...
no, add an extension method Step for it!
public static RangeWithStep Step(this Range rng, int step) => new(rng, step);
foreach (var i in 0..5.Step(2)) { ... }
That last one was also missing "public class var" to demonstrate the full sillyness of context-based keywords
I used the Monitor technique to replace System.Net.Quic with OpenSSL and QUIC Datagram support on Windows 10 back in .Net 6 when .Net 7 was in preview and only supported Windows 11 with TLS 1.3 in SChannel. Used with conditional preprocessor to target different versions of runtimes.
Monitor.Enter is the funniest thing I've ever seen so far 🤣
can you make a video of ActionResult, IActionResult, IResult and Result and the differences. Why and When we need to use each case?
The most insane thing I've done in C#? One time, I reversed an array without using Stack Overflow
Only insane if that took you less time than another developer looking it up and spending the time on something else ;)
The first one has the same behavior as the Path class in the Python pathlib library.
The date and path joining tricks might come from someone trying to replicate the C++ standard library, those features were standardized relatively recently.
What about some IDisposable ones? Especially for the monitor thing. Await using lock = new Lock();
1. This technique is extremely useful in any language because it allows you to join values together in a clearly regulated way that's still easy to read. Use it where it clearly would make sense to, if you start overriding != to produce a class you really should be beat upside the head
6. Dear God, its type of crap that makes the most vexing parse sound trivial.
With the new "Extension Everything" feature coming in C# 13 we will be able to add the "/" operator directly to string, enabling this path concatenation hack without a helper class.
I remember working on PL/1 on the mainframe (I love that language with fond memories) but that 'async' example reminded me of something that broke my heart that you could do with PL/1... IF IFIF = IF THEN IFIFIF = IFIFIF; something to do with being able to use keywords when you probably shouldn't, it's just mischievous eh!
The Monitor stuff feels like some serious insanity
I like range extension to facilitate in foreach (not for integers, though), in fact, I don't quite understand why language don't have it by default - I mean, shouldn't ranges be iterable?
Path joining - I think this a (rare) valid use case for operator overloading - I see no harm here.
Rest is... well... no comment, lol
For the lock-monitor, you can do something like "smart" using
@03:04: a Microsoft manager probably won't showcase Jetbrains products, and only Jetbrains ReSharper / Rider can do certain refactorings. So I have doubts about that course's completeness (in terms of how to do them automatically).
9:06 is a nice technique for a malware to do some source code infection
duck typing in c# is extremely powerful, imagine what you can do if the switch operator/expression were able to resolve which "case" go into, by calling a special method (or a method on an interface, why not) on the type that need to be switched on....i hope they will do it one day.
I like the idea of piping file paths and I'd never thought about it. Is the only counterpoint that other developers will be confused by it? I'm tempted to keep using it.
Could I maybe use that monitor class override to use default timeout to lock keyword? So that all my locks would timeout, instead of wait indefinitely in case of deadlock?
I am now using IDisposable LockWithTimeout(this object lck) ... method and "using" keyword to do the locking, but this would be much, much pretier.
Basically you can create all sort of these interesting things with the power of Extensions.
That's why I loved C#.
Monitor and the lock really surprised me
actually we learn everyday with you Nick, Thank you 😁😁
As I watch this, David Letterman's "Stupid Pet Tricks" comes to mind.
The monitor thing could be useful to make a distributed lock. Immediately came to mind because I had to solve this exact problem just days ago
Very cool 👍
As a general rule, probably don't do most of these. But, conceptually, how they work can be interesting and useful.
Some of those things have such strong Ruby vibes!
When you were beginning to show the lock statement, I was thinking "will we try to goto into a locked lock statement maybe?".
It didn't turn out that way, but I'm curious if it's possible. 🤔
(So can goto be used to enter a lock)
Wasn't the raw object locking as a mutex the old canonical way of locking stuff in C#? If I remember correctly it was because having a separate object as the mutex instead of locking the container itself is cleaner and less error prone supposedly. I could see that make sense in wrapper containers that maintain multiple internal representations for example but I never really understood the argument more generally tbh.
I personally do not appreciate any kind of trickery. The simpler it is, the better.
11:06 Buffalo buffalo, Buffalo buffalo buffalo, buffalo Buffalo buffalo.
Well, another 6 insane interview questions
Insane C# techniques that no one in their right mind will use - except course the C# team, who will probably add them to the base language in the next release.
Actually / operator is used in some projects and is a very nice feature, for example in Nuke build automation library to provide path concatenation
@@volan4ik. I know - just joking that many recent C# versions have come with new language features that modify the look of the language quite drastically (mostly for the better, it is to be said). It's good though - it can be hard to justify adopting approaches that are unconventional, if beneficial, when they are just some random NuGet library; when they are implemented within the language itself there's not so much second guessing using them when they are useful.
maybe not insane, but using monads will do quite interesting transformation to your code and flow
Reminder that awaitable tasks are already monads in C# with language-level support.
Javascript developers are now really considering C# after this vid
Sir would love to see .net AI content..langchain vector search etc.. any plans?
Maybe you could show some things that are flat out impossible in C#.
C# is Turning Complete, so it's an empty list technically
@asedtf I know of one niche thing, hooking functions used by the GC such as GlobalMemoryStatusEx. Managed code does not like being run in that context and will throw.
So there are things.
You can really do anything with it, even GPU programming (there are some transpillers from IL to cuda code for example)
For example, if you use unsafe code, you can allocate and delete an instance of any class just like in C++
Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should.
Why would you not use am embedded language for file paths? It's common practice in Python, it's there in the standard library (pathlib.Path).
7:13
Well, this is not unrecommendable I think, some modern languages like Rust and Kotlin have similar ways of looping
Nice video. Where’d you get your haircut?
this video made me smile :)
The Monitor is really something. How does that even work, not complaining about existing class/conflict... Hmm
Mine is just evil, not proud. Create a Console class with WriteLine method that prints "Compilation error" in a project of a newbie that didn't know about namespaces.
whenever I see creative uses of opperator overloading I hear the jojo "ooooh noohh" meme in my head :P
0:46 SUBLIMINAL MESSAGING
They should have just made the range type enumerable. Not really sure why they didn't.
Can't wait to have some python nerd make a package called pysharp or Cspy or whatever, with all the magic stuffs x)
I cannot wait until async becomes a normal keyword and the async bs is no longer valid. Same goes for var, and many other contextual keywords that should no longer be accepted as valid identifiers.
the async stuff looks like a war crime
my favorite is Infinite await sequence:
Var ret = await await...... await 3;
This looks like something one could use to troll/haze the new hire. async async async is my favorite, the foreach-thing is almost usefull.
For the "extension nonsense" example, I got an even better approach:
var today= 30 / November / 2023;
Just define a helper struct with static readonly members for each month, overriding "/" operator accordingly. And also put a "global using static" for that struct, so the month names can be unqualified 🤣🤣🤣
The last thign with async async async... it scares me, because it turns C# into javascript LOL
Dude, what are you waiting for? Actually an async of an async of async of async of asynce...Yeah, totally describes the state I'm in, most of my life!
People are afraid of a lot of nightmare fuel. This is so cursed so it beats any horror to date.
Ha, didn't know `lock` had such semantics
When you do something for the heck of it.
6 cursed things and other possible C# warcrimes
6 ways to write cursed C#
Dude just made Ruby from C# by Extension methods xD
Time to make F# chaining operator
That async operator example looks ripe for creating knockoff computation expressions
This video is basically WAT by Gary Bernhardt, but redone in C# 😂
NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
this video made me scream "NOOOOOOOOOOO"
Monitor 'override' is really weird.
This is some cursed stuff dude.
Some smells Python lol so I hate it, but the lock() one is kinda cool.
loved first one with overload for /, also pipe was awesome, have to try it some time
yeah, code obfuscation by extension methods 😬
WTFs said while watching this video was out of range
i love the forech range the most its amazing
Enumerating a range of integers that way (a..b) actually sounds pretty neat. But I would need built-in support from the language out of the box, doing it custom like this is the smelliest of smells.