I've had this sitting in my source for a while: public static void Swap(ref T A, ref T B) (A, B) = (B, A); You can swap class and struct members like it's magic. Good in graphics programming, collision detection.
This is crazy, you can even make this an extension method! public static void Swap(this ref T left, ref T right) where T : struct { (left, right) = (right, left); }
As a amateur Common Lisp fan I relate this very much to how you use list of various length and types to transport data in a smooth way without using heavier implementations like arrays or objects. And I find myself using tuples in the same manner.
Fantastic video, and I agree with it whole-heartedly (also the feel of it, and I already "stole" your quote on having lost already if you need to defend). Yet - at 9:00 You say "safe by design", but I would like to add "and also quietly wrong by design". Whether that's acceptable depends heavily on the use case. I hope this software doesn't go in a self-driving car . ;-) However, and this is what many OOP-maximalists forget - what will the car do if it encounters an unhandled (because it was unexpected) exception? So there's no clear winner here on that front, and if in doubt I'd always say the more concise, more clear soluton wins. All in all there's many nuanced discussions to be had here; I make video games, generally I'd prefer the "return sensible data and never crash" approach of Zoran. Except for payment fulfillment code. I would not want my user who wanted to buy 100 iterations of something only receive the first couple of values that fit into the integral type backing the count and receive a transaction receipt for the full amount stating that everything went well. ;)
This video came after I got a comment like "this code is alien to me". If we look at the pace at which new syntax is added to C#, it is about one item every six months. I just don't see how someone who works in C# every day can come into the situation to say that something is alien after being around for 6-7 years.
@@zoran-horvat If that was me, I can't thank you enough for this video. This is incredibly insightful and useful. A day where you learn a new feature and can see where it can be used and the benefits is an extremely good day. Thank you.
I will generally start with a tuple, but often end up converting it to a record or struct as I want to give the type a name with meaningful method names.
Yes, that is a typical process. Tuples are very useful in prototyping and then, when you make sure that the design is fine, it pays off to introduce strong types for the concepts you share between classes or return from public methods. Tuples remain useful among private methods in a class.
I like this feature and how they improved it over the "item1/item2". It feels cleaner to me (most of the time) if items being returned are in the return of a function. To me, "(int quotient, int remainder) = Divide(int a, int b)" is cleaner than "int quotient = Divide(int a, int b, out int remainder)". I wonder if this is best practice or not.
Thanks for the reminder to use these and that they are acceptable to use. I was just thinking about this today and was going to use it, but I changed my mind. BTW - I like the "This is an opinionated implementation." I know some people who would like this (like myself also) and others who wouldn't. Sometimes, when 75% of the developers like it a certain way, I will always go that way...also depends how I feel that day. (I not sure if this falls under those 75% thing.)
I have to admit that prior to your previous video I'd only seen and used Tuple and not ValueTuple. I've used System.Tuple a fair few times but it was very clunky and if I understand correctly, it looks like ValueTuple is like a mutable struct, is a value type, and exposes it's items as fields rather than properties? That's awesome! The syntax looked weird to me at first but I can see how useful this is. I will certainly use this.
System.Tuoke is an old type which I only used a few times and gave up entirely. But when ValueTuple type appeared, it was an entirely new story, 100% oriented towards performance and simplicity. Once we got them, it turned out that it is nearly impossible to write custom code that performs better, which means that we have all the reasons to always use the new tuples.
This should make it easier to organize code and handle local variables that change state throughout your methods. But the length of the tuple must be not too long, spotting errors in tuples of length of 3 should be okay, if there are like to say an example of many variables - seven variables you set like this : (t1, t2, t3, t4, t5, t6, t7) = (v1, v2, v3, v4,v5,v6,v7); Well errors might pop up, but trying to juggle with 7 variables separated with newlines and set one by one is not easy either.
That's why I'm subbed. I'm learning loads even though the coding style is very different to mine. The line "(a, b) = a == 0 ? (1, 0) : (a + b, a);" has five operators, I'd probably have written it like: (a, b) = a == 0 ? (1, 0) : (a + b, a); However, style aside I'm really seeing some value Zoran's videos.
A tuple is an ideal datatype for a _Point_ containing *x, y* members, but it falls apart if you decide to represent _Voxel_ containing an additional *z* member. A strongly typed static list, *(int a, int b, string c, float d)* with the same decomposition and list comprehension features as a _Tuple_ *(a, b, c, d) = (1, 2, "apple", 0.5)* would be far more useful.
@@bobweiram6321 It seems like you are taking "tuple" as synonymous to "pair", which it is not. The tuple implementation in any programming language, including C#, corresponds to what we call N-tuple in mathematics. Of course you can define a triplet, a quadruplet, a pentuplet or anything you wish using C# tuples syntax. Though it starts hurting clarity as you keep piling up new components, the syntax will not stand in the way.
Sorry, was there something in this video about using tuples? I got completely distracted by writing the Fibonacci algorithm without recursion. I froze up during an interview while trying to write it out on a whiteboard, so whenever someone uses it as an example, I'm like Frodo putting the Ring on one too many times.
I've used tuples in a service where I had to execute parallel tasks and aggregate the responses of all of them, then map them based on the responses of the tasks, this was wrapped in a switch expression. (I explain it badly, but there was no logic in the mapper)
I generally agree with Tuples being underused. While shorter doesn't imply easier to read, reassigning multiple values where traditinally helper variables are required help keeping the code more tidy and take little time to adjust to. However I disagree with the final part, starting at 9:58 'Will defensive coding return to my design?': the var keyword conceals the type of "number" changing from int to a string during rewriting. For a user-interactive command line application, I have no issues with the presented solution since the result is converted to a string anyways, in this specific case, the code is clean. However conceptually in most use-cases the program will get an input n (likely not as user input in terminal) and will have to deal with the number (or sequence in this example) and not it's string representation. In that moment the program will have to defend against the input number being too high unless n is known to be small enough. I believe that a method's definition should indicate that it may not produce the correct result based on it's input (for example it could provide an out parameter (bool sequenceFinished or int finalIndex)). If it does not, it may cause worse consequences than an Exception "just" crashing the program.
Well, yes. Once you start sending objects between objects, tuples are not a good vehicle. That is where they should attain a strong type, such as a record.
That is perfectly fine. Actually, I normally use tuples in private members and records in public. It is because a tuple is assignable to any tuple with the same shape, even when that would model a different concept that only has the same component types by chance. The same assignment is not valid in records because those would be the two separate types.
The only nitpick I have is that you changed the behavior of the program. The initial version put the last Fibonacci number in brackets, while the tuple version didn't. You could probably fix that in the Select() statement, using a conditional (whether f.n == n) to choose the formatting. So no real change to the program flow.
@@zoran-horvat Maybe but there is still plenty of legacy support going on. Took us years to get off Winforms/Framework, even now we still have some legacy systems to support. Dies hard, and people don't necessarily keep-up-to-date, not everyone is like you and me! A vendor recently told me that only 3% per year of Framework based systems are being upgraded. A bit of a concern given that support runs out 2026. BTW I programmed a new feature with tupples today, you'll be glad to hear. Yes, sure does compress the code.
This approach on your example reminds me ".reduce()" method in javascript and gimme an excellent idea. Thx ! Also, I got a question (sorry if it seems silly): tuples are very similar to dictionary, right ? Whats the difference between both where tuples can shinny more ?
Do you mean that Tuples are similar to KeyValuePair that is used in dictionaries? You can create a Tuple that has more than 2 elements and assign different names to them not just "Key" and "Value".
@@Tymon0000 Indeed. However, using tuples with more than 3 values may compromise the understanding of code, IMO. But, yes, it's possible to assign more than 2 values .
@@zoran-horvat no no I get it, I love it (although I can imagine someone arguing that it mildly contradicts your own advice to use strong types in business modelling instead of using value types), it just struck me randomly how strange programming languages can look.
@@nathanel1313 That is true. Languages change towards a more condensed form. What strikes me personally is how close the languages today are becoming to what ML did in the 1970s or so. It is the predecessor of OCaml and, subsequently, F#, but now C# is spring much of its style. It almost looks like reinventing the same simplicity again, 50 years later.