How type deconstruction keeps your C# code clean
Vložit
- čas přidán 15. 12. 2021
- Check out my courses: dometrain.com
Become a Patreon and get source code access: / nickchapsas
Hello everybody I'm Nick and in this video I will talk a feature called deconstruction in C#. It is a feature I really see underutilized and I'd like to change that by showing you how awesome it is.
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: bit.ly/ChapsasGitHub
Follow me on Twitter: bit.ly/ChapsasTwitter
Connect on LinkedIn: bit.ly/ChapsasLinkedIn
Keep coding merch: keepcoding.shop
#csharp #dotnet
If you've been watching Nick at all, you'd know he loves the numbers 69 and 420 for unknown reasons
69 dudes! m.czcams.com/video/zYWT4uYOPvs/video.html
What do you mean, he loves them? He always picks completely random numbers.
@@kippie86 random from set (69,420) 👍
Lol I just commented about the same exact thing.
So casual. 😂
Something to note; deconstructors were originally shipped with the var (x, y) syntax, it was only later when they added full support for the (var x, y) syntax, where you can have mixed assignments and declarations from a deconstruction, say if y was already declared you can now only declare x from the same deconstruction you assign the value to y.
I didn’t know Key-Value pair had a deconstructor, that’s gonna be really useful when iterating over a Dictionary
I already use deconstruction a lot and didn't think I'd learn anything from this but then you did a deconstruct extension method and that I didn't know about.
Can u make a video about Finalizers vs Disposables?
Most people getting into the topic dont understand the difference or the use case
This!
Yes..
Disposable clear everything right after exits using statement
Finalizers will clear everything in some unspecified future when GC decides there's not enough memory,
So in short use Disposable when it's critical to clear everything up right after using it like open files, database connection
Up!
Generally, finalizers should be avoided as much as possible for they have negative impact on GC.
Seems like it would be really easy to mess up variable ordering/mapping when calling the deconstructor. The deconstructor is also invisible and might confuse someone new to the code base. Deconstructors should probably be left to tuples and key value pairs.
If Nick Chapsas and Tim Corey had a baby that baby would make C# tutorial videos at exactly the right pace.
Speed Tim’s playback up or slow mine down. There is no perfect pace for everyone
That's funny, I've used it yesterday because my IDE suggested it when switching from tuples to a struct and now I'll get it explained it a bit in more detail..
Thanks a lot for the background info!
I indeed didn't knew the deconstruction feature. Thank you for explaining it!
Thank you Nick! This one turns out to be very handy. I will apply that tomorrow ASAP.
Wow, very informative for different scenarios. Thank much
Never know all this C# code until you deconstruct it hahaha. Thanks Nick!
I have been doing this in JavaScript, and finally it comes in C#. Thanks for sharing!
When you think about it a Deconstructor is the opposite of a signed Constructor.
The Constructor injects data into the object for internal use.
The Deconstructor ejects data from the object for external use.
I hope one day Nick will make a behind the scenes video involving the 420s and 69s he has been hinting for almost all his videos.
I've been wondering how to do this for so long. Thank you!
This is awesome! Thanks for sharing.
Thank you for an informative and inspiring video!
One of the new things I learned from this video... DateOnly
Thanks Nick. ❤️❤️❤️
So simple, thank you.
So glad to know how C# does this (and what they call it)
bloody hell I didn't know we can extend to deconstruct :D good one dude
Deconstruct can be overloaded as well, such that the consumer of your API can be as granular as they want without having to discard.
Good video. Thanks!
Nice so destructuring from js is brought in.
"Just a number!"
Nice!
Sincerly, i consider that deconstructor a new way to get code less readable. If i watch the code outside visual studio(so no intellisenze, no tooltip, etc..), it' quite weird understand variable types, and other stuff.
That's a good point. Readability is key in the development of clean code. If you look at my coding there will always
be an initialisation section at the top of the program where you will find a reference to variables used in the program.
Particularly reference types. It doesn't matter what teh language is. It's a basic principle. Of structured coding.
I am wondering as I look at that line (var fullName, var dateOfBirth) = nick what the scope of those two variables are.
Still, it's worth having a play around with this stuff don't you think? I will give it a go.
I agree. Seems like a feature to just add more confusion. I don't like it when code is less readable. Such features like this are handy for personal projects but not real projects where you work in teams. Imagine having to check the class just to see what the objects may be to save a couple of seconds lol.
It's not difficult to write extremely complex and contrived code. The reason I don't is because, as a freelancer, I am aware that some poor sap would have to figure it out some way down the line after I have delivered the project and moved on. Lean and clean.
And again the JavaScript in me is happy!😂 Thanks Nick!
I wish it worked more like it does in JavaScript where you could arbitrarily just name a property you want from an object and even give it an alias variable name and it works. Here in C# it feels much more limited and you have to know in advance the order in which to ask for your properties, or even know internals about the order in which a deconstructor method returns its parameters.
I like how in the last few videos I've watched you've defaulted to using values like 420 and 69 without even thinking about it. 😄
He was definitely thinking about it on an aysnc thread
Hey Nick, love your videos. Would you please do a video about named pipes? Example: one searcher thread, and multiple worker threads that send data to the searcher thread or process.
Named pipes is a heavy OS construct.. Use it for communication between processes or app domains, not between threads in a single program. It'll work, but it's like using a 10 ton truck for moving a single bottle of soda.
Nice
"Born in 420" 🍁 Excellent video Nick!
69, just a number lol ( czcams.com/video/iqbgXgrEB4M/video.html )
In some cases I feel like deconstruction can cause other problems. If you deconstruct record Book(name, isbn) to var (name, isbn), but then later decide to re-order the deconstructor, you'd break all the code doing that deconstruction with no compiler warnings or errors. If you just worked with the Book object with it's Book.Name and Book.Isbn properties this would never happen.
yes, that'd why I'm hesitant to use it...
There's stuff where it is safe, like keyvaluepair. But I even put parameter names in if the type is a common type, just to be sure no body effs it up...
btw: it is really cool for switch expressions, but still hesitant
This comparison feels wrong. It would be fair to compare it to code that extracts the values into different variables, one line at a time, and see that code cause the same issues you describe. But comparing it to code that never extracted the values at all, isn't even the same thing, it is just saying that values should never be extracted because of this issue you described?
Same can happen with tuples
This is one of the pitfalls, especially with dates. What if you expect (day, month, year) but the Deconstruct says (year, month, day). Oops.
Take it as encouragement to overcome the bad habit of using primitives for everything and create more types! Help the compiler help you!
Nick, can you make video comparing structs, records and record structs? It can be interesting theme.
yeah and don't forget classes and stackalloc ...
He already does that video - czcams.com/video/9v6RENPk5iM/video.html
Can you explain the = default!; feature on the FullName? I have not seen that syntax before.
The string is being initialised to its default value, which is null for reference types.
However, the compiler complains because a non-nullable property is being assigned null. To get around that, the null-forgiving operator (!) is used to tell the compiler “it’s ok, this value definitely won’t be null when I use it, trust me, I got this.”
Default keyword is an explicit declaration of assigning the default value for a given type to a variable. So string is null here, book false, int would be 0, objects are null, etc.
! Is to get around the nullable check warnings
Thx. Ngl, not had a use case for the null forgiving operator yet
@@JohnKerrashVirgo simple. Just add enable to the PropertyGroup in the project file. After the 100th warning about field or property being null, you'll probably start using the ! too 😁
whoo. Very nice. Of course JavaScript has already de Destructering. I didn't knew C# has it now too :)
TypeScript does this so much better no need to declare a destructor method. It just feels like this is overkill.
I just ran a small benchmark test. Having an explicit deconstruct method that returns a tuple (which is easy to deconstruct) is faster than using the suggested method here. Of course, it's just nanosecond stuff.
That was awesome !!!
Can you please make a video on following topics:-
1. yield
2. Asynchronous stream (video streaming)
Thanks in advance
Both of them are coming in 2022
Do you have any video about touples? I'm always worried if the touple I'm creating is going to allocate in the heap in performance critical scenarios
If you use Tuple, then it's on the heap. They're classes.
If you use
(string name, int age) GetPersonInfo(...)
it acts like a struct. They're basically ValueTuple but with a bit of compiler magic throw in.
If it's a class member it'll be on the heap along with the rest of the data of the object, but if it's a method variable it will be on the stack. They can also be a member of a struct, in which case it'll be wherever the struct resides.
Cool to watch those new features but in the same time stuck to 7.3 at work lol
I need to ask, I have seen all the videos on this channel this year.
How do you learn all these useful little things?
I am in C# only since January, I know I need to learn a lot every day, but these tips aren't in other videos or books that I am reading.
How do you learn all these magic codes?
Thank you to share, I appreciate a lot your content, I will happy when I will buy your course :) (it's a present :P)
I follow the published notes for each language release or framework patch/version and I try them out.
I learned the same sorts of things about JavaScript just by writing a lot of little projects. You come across libraries and examples from others that might have little tricks you didn't know. Also as Nick said, follow the changes to your language.
Don't worry.. I've been using c# for more than 20 years, and I always learn new stuff. The fact that there's new features every year keeps it interesting 😀
C# became into JS that fast I didn't see it coming.
This would be a great feature if they made it a feature of the language, where it automatically took a deconstruction like "var (first = FirstName, last = LastName) from person" and converted it under the hood to a typical "var first = person.FirstName; var last = person.LastName". Otherwise 2 things happen: 1) When you refactor code, you could end up with naming inconsistencies, and 2) As others have pointed out, if it uses a method and the parameter order changes it could swap the values into the deconstructed parameters and you would never know it. Operator ordering is why I tend to prefer passing a single complex type for arguments to methods that require multiple arguments these days. Its a pain, but fewer issues when refactoring.
Yeah, I'm really surprised by how flawed this feature is. 1. A method with a magic name is required. 2. The original class is supposed to know which vars the calling code would be interested in? 3. Writing extra extension methods only for the sake of this? Nah, I'm still not planning to use that. MS should really have a look at JS about how to do it. But yeah, the prop naming convention is different from local var naming convention. We are doomed!
I don’t really see the value in this feature either in cases where you need to write your own code for the deconstruction. However, all of these problems are avoided in record types, which generate the deconstructor for you, which is where the feature feels a little more sane.
born in 420, this one never gets old
Wow. I've been coding C# almost since it came out and I didn't know about this. Which version was this first in?
C# 7 I think. Same version tuples came out iirc.
@@pilotboba 👍
Hello, when you deconstruct a class does it dispose the class at the same time?
No, it has nothing to do with disposing. It is only about assigning some values to a bunch of variables.
Dude I’m exactly 6 days older than you but skill wise I feel like you are 6 years older than me!
What does default! do? Default for string would be null?
What does "string FullName {...} = default!" means?
It basically tells the compiler "don't worry I know I don't have a value for it right now, but assume that it's not null because I will provide a not-nullable string when i initialize it"
default represents the default value of a type. For string = null, for int = 0. public string FullName { get; init; } = default! is a immutable property and you can assign any value except null.
@@arkord76 You can still technically assign null, the compiler by default will only warn. It translates to "The non-null default value for this type"
What is the name of this feature? "default!"
@@nickchapsas Yes, you right. I realized it after I tested it. Thanks for the clarification.
Didn't even know it existed. Nich to have - but not really essential.🦄
This is so good. But 420 & 69 is funny though 😂
I understand the potential usefulness, however I think this could easily lead to code obfuscation. If you'd like to create variables to copy details into, I think it's better that the source variables are explicitly referenced. If I'm reading code, I don't want to have to step into whatever "nick" is to know that it's simply returning some unmodified data (especially when using var), and not formatting it or performing any other kinds of operations. I think something like:
var (firstName, dob) = (nick.firstName, nick.dob);
is more readable. Even better, don't copy memory if you don't plan to make modifications or operations on it and the data isn't expected to change while you're performing your operations. If you simply want to write the first name and date of birth in a string, access the object fields directly.
In large code sqeuences where several of those operatiosn are repeated from severl sources that leads to smaller and cleaner code. Somethign that is not clearer with ONE execution might be the most clear way when you are doing it 10 times.
@@tiagodagostini Yeah I agree that it can be useful and neater, but in most cases not. Just another tool to keep into the back of your head.
Cool! You are born exactly 20 years after me! 20/04/1973 :D
:'(
wait what happens with instance of this object after deconstruction ? do we have two instance of this element in memory ?
Nothing changes since the deconstructor only reads from instance.
It's basically the inverse of a constructor.
Instead of taking in x, y, and z and spitting out something, it takes in that something and returns x, y, and z.
Jesus, I don't know C# anymore, haha. Thanks!
Unless there is some serious IDE support that will tell you what deconstructors exist and tells you what to actually expect from the values you get back (i.e. Actual API documentation) I really hope this feature doesn't take off because it's just another level of hidden magic complexity that makes code harder to read and understand. Now you have code that looks like an assignment and instead really is a method call. And if your video is to be believed, the only way to find out what method the code actually jumps into is to run the code through the debugger. That sounds like development hell to me. And for what? So that you can be so UNBELIEVABLY lazy that a book.Deconstruct(out var title, out var isbn) was just too much to ask for. Seriously?
And with the automatically implemented deconstructors in record types it just becomes plain evil and a giant problem. Say we remove the isbn from the book record and instead replace it with a second string property that contains the author.
In normal code that actually calls the property all uses of the isbn property will break (because of course that property no longer exists) and the compiler will tell you everything you need to fix.
If you use the deconstructor: well bad luck for you all usages just continue to work because both the number of arguments and the data types are identical. It just so happens that all your code that expects an ISBN is now being fed author names and stuff randomly breaks. If you're very lucky by actually crashing. If you're not lucky this now will pollute and corrupt your database by putting data into the wrong collums. Have fun explaining to IRS auditors why the address column in your billing table now suddenly contains your customer's email addresses instead. (you better be the God of unit testing to catch all of these)
And the best part. The IDE has no chance of detecting this so now you have to go through all your code manually and find all the offending spots and fix them by hand. Sounds like development hell for me. Especially since this can happen to you unexpectedly if the record type you're consuming is from an external library. Update the nuget package - > everything compiles but suddenly you have seemingly random unexpected behavior. And again for what? So the programmer can be just a tiny bit more lazy?
Code is a list of statements. They express intent. So put the intent into the statement instead of hiding it behind a bunch of roslyn pixie fairy dust. The devs that will have to clean your code up after you moved on to build the next microservice in your next contract will be ever so thankful.
Oh and I just thought of another reason not to use it on record types. If you add another property to a record (so a purely additive change that therefore shouldn't be breaking) that now becomes a breaking change and forces you to touch every spot where that deconstructor was used. Yay....
Totally agree. Order and type are not sufficient things to deconstruct on and leave a lot of chances to flip args. Feature would be a lot safer and readable if you had to deconstruct based on field names (like you can in js) rather than based on field order.
I had absolutely the same thoughts.
I love desconstruction/destructuring but I really don't like having to write a method for it. Should just be a part of the language that works. Even C++ did it better in my opinion. Although the crown has to probably go to JavaScript and/or Kotlin in that regard.
Feels weird to give JavaScript credit for anything but yeah, it’s destruction is definitely top of the chain
I think they should make Deconstruct as a keyword for methods like virtual or something
This feels quite beyond me currently, after the first few lines with a init setter and default, and then object creation without a constructor? all of this threw me for a loop as I've never seen anything like it. I swear, every time I feel like I'm finally getting the hang of programming I'm shown time and again I have absolutely no clue what I'm doing.
i'm personally not a fan of "magic phrases" in programming, as i feel it's like language-sanctioned stringly typed programming,
i won't be using this technique for that reason alone (instead, if i wanted to achieve this effect i would create a separate function that returns a tuple and must be called explicitly)
My problem here isn't the concept of deconstructing the object, but rather the fact that this function called "deconstruct" is used as if it was an implicit cast operator, these two things are not the same which breaks your (or at least my) ability to logically reason about what's happening,
to someone who doesn't know about this special case, you have a named function that's seemingly never referenced, and an implicit cast to a tuple that doesn't seem to have an associated implementation.
you HAVE to know that this "magic phrase" exists, and how it works in order to understand any code that uses this, that's rude (IMO) and a code smell
I know it will be a bad idea to have several deconstuctors, but how is overloading handled with such patterns and can you even have 2 of them for 1 class?
public void Deconstruct(out string firstName, out string lastName)
{
firstName = FirstName;
lastName = LastName;
}
public void Deconstruct(out string firstName)
{
firstName = FirstName;
}
what does the default! thing mean? I know that default assigns a value to its default state but I can't figure out what the exclamation mark means.
Edit: and what does the :Mdd mean?
! is a .NET 6 thing I believe and from what I’ve seen so far, it seems to tell the compiler that this value won’t be null so that it doesn’t warn you later on. Please someone correct me if I’m wrong.
:Mdd is like calling dateOnly.ToString(“Mdd”) where M represents month (lowercase m is for minutes) and d represents day. d will not add trailing zeros if day is single digit / < 10 (e.g. 09), but dd will
It’s not a .NET 6 thing. It’s been in the language since C# 8. It tells the compiler that this value is definitely not null. In that context it just tells the compiler to stop complaining for an uninitialised string that isn’t marked as nullable
@@nickchapsas oh ok. But I don't quite understand what the :Mdd does. It is a shorthand for calling ToString.
But what is it exactly? Is it a method reference? It reminds me of the Java Syntax MyObject::myMethod.
@@tobilinz_ :Mdd is string formatting. The M stands for the month number and dd stands for the day. When you look at the output, it says: "born in 420", month 4 and day 20.
@@Grimlock1979 so the : means .ToString? Let's say I create a class. Can I convert it to a string using this too?
this is exactly what i needed 2 days ago! you are two days to late ;)
I'm finding it hard to come up with a scenario where I'd actually use deconstruction.
These things give me that uneasy feeling you get when you know something is a code smell, but can't quite articulate why yet.
So is the deconstruction method name reserved, then? Like when defining the extension method, for example.
Yes, it is pattern based. When you create a method with out parameters and you name it Deconstruct, the compiler wil recognize it and you will automatically be able to use the syntax with parenthesis.
Some other things are pattern based as well, like the Dispose() method. You don't need to implement IDisposable in order to use the 'using' syntax. Same with foreach and GetEnumerator().
@@Grimlock1979 Since when? I did not know C# did duck typing like that, will have to check it out.
What is really confusing me is why this is set up using out parameters when it seems like it would be far more obvious to return a tuple? is there a reason for this do you know?
Could you please also post your videos on LBRY?
I'm so mad that they have you define Deconstruct method and not just access public members via code lowering.
Js does it right. No defining Deconstruct for each desired combination. No _ to discard unneeded data.
The only great thing about c# way is extensions of 3rd party types - that's pretty neat.
What does that "!" do after "default"?
In the second example down, hope that helps: docs.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-variable-annotations
Known as the null forgiving operator: docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving
Also known as the dammit! operator.
When was Deconstructors added?
At the same time tuple syntax was added. C# 7.
Clean? - Maybe. Readable? - Definitely not.
This syntax is mostly confusing because it's like tuple and seeing it you'll slow down, so pls avoid it in your production code at all
Why would it slow you down?
born on 420 and his "random" number is 69.
I can see where u are going :D
Nice vid !
I don't know where I would need/use this. As with the tuples its a nice feature, but it just feels like bloating the language. Probaply something that I will try to use once or twice and then forget about it.
Key-Value pairs is where I always deconstruct because it makes it more readable IMO
Yea kvp with foreach is a rly good use case imo. I always use that
Deserved my like for the deconstruction, but deserved my sub for the 420/69.
it's just a number, a magic number 🙊
Now I know what deconstrusion is. But nobody would understand if I would start using it. Really not common this feature
Does the method name 'Deconstruction' matters?
You were born on same date as me, except I was born in 1998.
Still not sure why they didn't call it unpacking and avoid the confusing name.
I have a hunch that 69 is Nick's favourite number. It's in every example revolving around ints
I hate that C# requires a deconstructor method. This should just have worked out of the box based on conventions
Step in the wrong direction. A step from magic, unicorns, anonymous object everywhere and JavaScript 💩
Interesting video but I honestly don't find this very useful.
1) It only really works with small data types because if you were to use large data types, you'll be doing a lot of discarding, which will just make your code messier.
2) It requires you to know the order of parameters, and because of how it's laid out, the IDE also doesn't give any assistance. This is likely going to lead to a lot of code lookup.
If you can't figure out what the code is doing by simply reading it, then you're doing something wrong imo.
08:44 😀
I don't like it because you have to know the element in the correction position otherwise you could set your variables to the wrong thing and it's not entirely obvious when reviewing code that the variables have been swapped.
Generic Decostructors + Pattern Matching = Code-equivalent of erotica
Now I know which feature I will avoid using.
Peak comedy
this makes code complex in my eyes
I have used them from time to time. I thought they were tuples. Are they?
The deconstruction creates Tuples yeah
@@nickchapsas I thought as much. Been pretty much using them for a while now but always referred to them as tuples never as type deconstruction
@@deboayangbile1689 The object is deconstructed *into* a tuple.
So it calls a completely undedcorated method with no interface restrictions?
It's useful I suppose, but it's really out of place compared to the rest of the language.
how about 42? you have to ask the question!
you seems to prefer Rider over VS any reasons why
I have a video on that topic
@@nickchapsas will check it out
The name does not make sense at all with the purpose. It is neat (in C++ I would have to write a more complex member declaration (a conversion operator to a tuple), but they coudl have found a better name for that.
I like your numbers: 420, 69.
:Mdd Sneaky 😂
Well... comparing it to f.e. JavaScript/TypeScript deconstructing, it's reaaaally reallyyyyy weird and unreadable syntax. Like...
How on the first glance am I supposed to know that some method called Deconstruct (which is not even marked as reserved keyword in IDE lol... so I can assume that it could be normal method of some class), would be connected to this magic syntax
(var fullName, var dateOfBirth) = nick; (where this looks like method call without it's name mentioned... another wtf)
Discarding is also weird
It's super super weird