Intro to Tuples in C# In 10 Minutes or Less
Вставка
- Опубліковано 15 лис 2024
- Tuples can be a big improvement to how you write certain code in C#. See how in this 10-Minute Training video.
Full Courses: iamtimcorey.com
Source Code: leadmagnets.ap...
Mailing List: signup.iamtimc...
Interestingly, 15 minutes ago I just found out that this existed, and used it in an async function that needed 2 returns (because it's async I couldn't use ref or out)! That was a lifesaver! Your video was very good to explain better! Thanks!
Thanks for sharing!
Tuples are handy but can be abused. They are fine for closely related values like coordinates etc. But don't use in place of a legitimate data structure like a class or struct. They look ugly and can be hard to read
@@rajm1976 It was even uglier when I didn't know you could name variables! :P... I had to use .Value1 and .Value2, horrible. Thanks to Tim now I found out that there is a way to name and it has already improved A LOT!
@@CezarWagenheimer imo if you are struggling with two returns it indicates something is wrong from a design point of view. Nothing can be done with tuples that can't be done with a struct or class.
@@rajm1976 This is true, but the same could be said of anonymous classes and records. The features were added for quality of life improvements. Every feature can be abused, but it's better to have them than to not (*cough*Java*cough*).
I clicked on this video randomly while I was watching C# videos, And I just found out that that solves so many problems. I had Writing C# methods.
Awesome! I am glad it was helpful.
Tuples are great for passing multiple variables between methods, but be careful not to overdo it. Classes and structs (including their record variants) should be preferred in most scenarios, as they enforce semantic type safety. For example, a Person tuple defined as [string Name] and [int Age] would be interchangeable with a House tuple defined as [string StreetName] and [int HouseNumber], as they both just consist of a string and an int.
Tuples are great if the two returned variables aren't semantically related, or for an internal method where you don't want to have to define a distinct type for a single method.
Tuples also have the advantage of working as the return type of an async method, as ref and out parameters cannot be declared in an async method.
Agree completely. Nothing is more annoying than trying to peek the definition of a tuple only to find it's not a class and being returned in a function.
I am a newbie and I'd request your opinion on this. I am not that familiar with the concept of functional programming, but the feel I have from 'ref' and 'out' is that they hurt the clarity of the flow, and 'tuple' there somehow feels like it provides a method to code in the functional programming fashion. Am I correct?
@@smarthumanism3221 Tuples are fine imo for closely related values and where a struct or class is not required. You can return any number of values in a tuple and this is where it can be abused imo
@@rajm1976 Thanks for the reply.
@@smarthumanism3221 I'd put it that way: (Named!) tuples are of great value (also) in functional programming, as they can easily and elegantly form a _result_ of a function that not necessarily consists of only one single domain type or if the result includes kind of (mixture of) additional or just temporary information as the additional state "isValid" in Tims example. This way it helps to chain functions and transmit for instance "calculation result(s)" and "status", based on (long lived) domain types and (short lived) state. This approach with (even complex) state information can perhaps also be seen as better way than simpel null return values or even misuse of exceptions for state propagation.
Personally, as long as no one tries to replace "solid" domain types by tuples, I don't see much risk of "over-use". In functional environment variables imho anyway tend to short live and are "processed"/"consumed" rather than "transmitted"/"traversed" in their entirety (Functional: immutable ->construction/deconstruction).
Otherwise, everything can be misused.
(string address, bool valid) =
Can be simplified to:
var (address, valid) =
This is especially useful when the tuple has more elements, or when the type names are long. You can also use var for each individual element, but I prefer to just auto infer all the types at once.
Yep, you can do that too.
But it doesn't explicitly express what types are returned, so in the perspective of managing a big project, it might hurt the intuitive understanding.
@@smarthumanism3221 Imagine having nested tuples and typing out all those explicit names. I'd much rather see var with descriptive names in my codebase.
@@protox4 Your point is that if it's too complicated it's better to shorten the declarations. I disagree since this notation makes sense only when there's not too many values to be returned. I would agree more with the explicit declaration instead of var, but it's still valid, I wouldn't reject your PR for that ;)
Hi Tim,
I simply wanted to say, as someone who only looks at programming as a hobby, that I really appreciate the many formats that you have put in place over the years. These shorter videos are a great addition to you longer in-depth videos.
Kudos to you and your team for your great work!
Thanks for the support and encouragement
I'm just starting my journey in teaching myself C# and I'm just glad I was able to understand what you meant. This definitely seems like a useful feature and I will try to incorporate it into one of my projects. Thanks! Just gained another sub!
I am glad it was so helpful.
Definitely one of my favorite new features. Really hated the ceremony of creating short-lived classes just so I can meaningfully package data within a function.
Yep, this is a lot better than single-use classes.
I had no idea we could do that honestly. My use of Tuples was limited to a Tuple which works too, but seeing it implicitly is a very nice and readable addition. I like that. Thanks Tim!
You are welcome.
I have used them here and there - and the way you show here, as a way to return multiple values without having to create a class.
Great!
My senior eng. told me as itnern i shuold learn from your videos and they are really awesome and helpful thank you👏
You are welcome.
I really like your 10 minutes series. I would love to see something similiar for Azure.
Just short clips of certain services or configurations.
Thanks for the suggestion.
You may want to add this idea to Tim's suggestion App so others can vote it up. It sounds like a great idea. suggestions.iamtimcorey.com/
You are my favourite c# channel, you make c# interesting
Thank you!
Thanks for these videos Tim, they are great for quick refreshers or intros to new concepts, really appreciate the effort.
I haven't checked if you have one yet and will after this, but a video like this for delegates would be awesome.
Edit: There's an hour long one, awesome.
Glad you found it.
Very nice! I was just trying to work out how to get multiple values out of a method to avoid code duplication and this video pops up. Perfect! Works beautifully.
Excellent!
i never used a discard in tuples. Now i know i can. Thank you.
that's the accurate resolution,
Great!
With their first appearance I was strongly against tuple, because without the ability to name the "items" I have seen terrible examples of (mis-)usage. Tuples of e.g. many strings and combined with remote points of processing in code and a lack of testing were a nightmare for refactoring. Since naming could at least mitigate the issue a little bit and (not at least) with the advent of functional programming and advances in pattern matching, tuple became more and more handy. I use them (for quite some time), mixed also with anonymous types, wherever I feel like in processing - but of course not as replacement of "solid" domain types that are long lived and worth a definition of their own.
Thanks for shining a light on that Tim!
You are welcome.
Really cleared things up for me. I was scratching my head about tuples: wouldn't it be better to use a struct, or put all of this into a class? Then your example of returning multiple values from a method solidified the point. Yes, in certain cases tuples actually make sense.
I am glad it was helpful.
Tuples are a set of two or more variables that only make sense together, not apart. For example a return value and an error. When the error is not null, the return value should be null, and vice-versa. And this way you don't have to use out or ref param modifiers which I think IIRC are slower in C# (which might seem counterintuitive) in most cases and also might break immutability in functional code.
This is exactly what i needed for retuning multiple resultsets from the dapper queryMultiple!!!
Excellent!
Thanks again for this short and clear explanation of tuples in C#.
I didn't like tuples (in other languages) because I was always missing the discriptor.
Now I have seen it can be done differently ;)
Very nice.
Glad it was helpful!
Tim, thanks for this video, very helpful. Love the 10min format. Keep them coming
You are welcome.
A really helpfull video. I think not fully complete as far as the subject goes but in 10 minutes you get a clear understanding of how to use them, which is what you set out for. I think you've perfectly hit your goal :D
Well, coming from Python... this is NOT what I expected a Tuple to be in C# :D Thank you Tim, for the clarity you provide! I know what I am getting for X-Mas this year... C# Masterclass!
Thank you again! 🙏
I am glad it was so helpful.
I love your 10mims videos, am able to learn something new whiles taking a short break. It's excellent 👍.
Great!
I love seeing C# evolving! Thanks again Tim. You're the best!
You are welcome.
Awesome little video, short enough to my liking, I watched it while cooking dinner. 😊
Glad you enjoyed it.
love this; clear and concise
Thanks for watching and endorsing
This is exactly the convention used in GoLang. The only thing annoying is the current C# convention is to use lower casing for tupple property names, which doesn’t match the convention anywhere for classes or struct public properties .
Awesome feature now available on C#! Thanks for the introduction to it!
You're welcome!
Definitely useful for returning multiple parameters from a method!
Yep.
OOhhh.... I am loving this. Definitely a fan! You still maintain strong types, but you get flexibility of return type from your method.
Glad you find the information useful!
@@tomthelestaff-iamtimcorey7597 greetings, Tom; and welcome to the community!
YESS finally i can return multiple values AND EVEN choose which one i want to use thanks ( i know there's more to it but that's what i need it for so thats what im using it for )
I am glad it will be helpful.
I was most impressed by swapping variables with (x,y ) = (y,x). .. the rest is just; saving you to define your own structs manually.
Right. They are for simplicity. You don't need to declare a struct only to use it once in one class.
Timely video for me. I was just about to use tuples for the first time at work.
Excellent!
Thanks for the new series Tim.
You are welcome.
Really useful.. simple and clear explanation.. keep doing
Great!
Awesome, didn't know this existed and have had cases where I needed this. Great vid!
Thanks!
Nice. Was just using the ref method yesterday for a function - never knew this existed.
I'm glad you learned something new.
I think I still prefer out or ref because there are just fewer variables to keep track of but it's great to have the knowledge of this option. Thanks!
For this particular example, the TryGet pattern returning a bool with an out parameter makes more sense. Tuples are really more useful when you need to return _actual_ grouped data, more than just an extra bool.
I'm not sure I see the benefit of less variables since ref can lead to some unexpected results. I prefer to have my value types remain value types, which is what tuples allow.
Nice one. I use them all the time but I didn't know you can use them this way. I use a lot of Tuple collections like:
List lstOfThings= new List
and then after filling the list something like this:
foreach (Tuple t in lstOfThings)
{ ... }
Why don’t you use classes in these instances?
I thought about it but its something I only use in one place. So I thought why create a class if ill never use it again. Plus I already have over 100 classes in this project.
i thought tuples were this complex mysterious thing. thank you for breaking it down. not so scary after all
Very clear explanation like always.
Thank you!
Thank You! It's really great explanation! I like your 10 minutes sessions! Keep it up , please, it's got the point!
Thanks, will do!
@@IAmTimCorey Could you make a video with "ref" and "out" in C# ? I'm still confused about how, when should use them. Thanks
I'll add that to the suggestion list.
Very cool. Very very cool. Thank you so much for sharing with us your knowledge. It really means a lot. Thank you
You are welcome.
Keep going I liked this 10 minute - series 👏
Thanks, will do!
I'm enjoying going through these short videos and learning a lot... I need to upgrade to .Net 6.
That said, I'm familiar with Tuples from Python and it's much better than being limited to a single return value.
So how about this? On occasion, I've needed a Dictionary with a very simple value, but more than a single primitive type so I did something like this:
public class Tuple1
{
// This works... When you need it, you need it...
struct struct1 { public string string1; public string string2; }
Dictionary struct1d = new Dictionary();
// So is there any way to do that with a tuple? I tried a bunch of weird semantics like:
Tuple tuple = Tuple;
Dictionary tup = new Dictionary();
}
It didn't like that or even more reasonable variations. Is there any way to do something like that? Is there any reason to do anything like that?
.... You think that's weird, you should see my projects :)
Thanks for the tip. Hoping it stirs up some discussion.
great video as always, Tim! love the 10 mins or less series. I'm really interested in diving into the functional side of c#, I can see some really great uses for it, but I'm struggling with how to do a lot of functional composition, while mixing that with Async and UI code... it feels like the edges of the old maps that just read "There be dragons here" lol. That probably isn't a 10 min video..
something about possibly asynchronous nested funcs just.. good heavens. mah brain hurts.
I will add it to the list. Thanks for the suggestion.
Tim do you have a video about the actual console window and the cool stuff you can do with the console from within the app, and the tool bars
Thanks a lot Tim for this excellent video as usual. So, what I understood a tuple is a variable can hold more than one datatype values at the same time. And tuple values can be read as properties of the tuple. Tuples are unchangeable, or immutable. Please correct me if I'm wrong.
All correct except the last. Tuples are just like any other variable. The values can be changed later on. Think of them like really simple classes with just properties.
@@IAmTimCorey Thanks a lot for your kind response.
Didn't know this! Thanks for sharing!
You are welcome.
I've seen it used for comparing big lists one to one where there is parring (source vs destination)
Great video as usual.
I have a question for you Tim. I have a web application using blazor server framework. I want to automatically logout my user if their session is idle for let's say more than 30 minutes. I am using individual authentication that comes with the template. I would appreciate it if you could point me to the right direction.
Thanks.
It depends on what you mean by logging them out. You can set up the authentication so that it only keeps them logged in for 30 minutes and lets them renew within that 30 minutes. That is a configuration setting in the identity system. I don't have an example video to show you that, but if you look at the documentation for Identity then you should find what you are looking for.
Hey Tim.
Is there any advantage for Tuples over anonymous types or vise versa?
For example:
var listOfPairs = from boy in boys
join girl in girls
on boy.HairColor equals girl.EyeColor
select (boy, girl);
var listOfPairs = from boy in boys
join girl in girls
on boy.HairColor equals girl.EyeColor
select new
{
boy,
girl
};
foreach(var pair in listOfPairs)
{
DoSomething(pair.boy, pair.girl);
}
Thanks :-)
I'm not sure, but I guess an anonymous object will generate code, other than being a reference type too and stored on the heap. A tuple is a struct, so it will be stored on the stack. So I guess it will make a slight difference depending on your use case, but I'd say anonymous objects would be more effective with collections and tuples for grouping data, to return from a method for example.
Hi Tim ! Please can you make a video about signalr (real time chat) with different users?
I will add it to the list. Thanks for the suggestion.
@@IAmTimCorey thank you very much !
@@IAmTimCorey I wonder how long the list is right now
I've used deconstructing in JavaScript and php. It's really helpull when you want to return multiple values 👍 but in c# I learned today and it's very similar
I'm glad you learned something new.
Never heard of them before, but React uses stuff like this. When did C# start using these? What version of the Framework?
Tuples were in C# 8 or 9 but they were expanded on in C# 10, which works with .NET 6.
What do you think about using Tuples instead of composition?
tuples were bad before naming tuples.. ;d now i love them. I do it whereever i can
Glad you like them.
Good to know about Tuples
Great!
What is your recommendation for when we have to return more values? Say 15 values. The idea behind this, is that you would, for some reason, retrieve data from multiple tables in your SQL database, but you'd have to present them in one single DataGrid. In that case, a tuple of 15 different columns wouldn't be as handy, as having a separate class that interconnects those columns. I would love your input in this issue, it's something I've given a lot of thought into and my google search hasn't lead me to a better solution than creating the new class.
Definitely a class. That's what they are designed for. It doesn't matter how many tables the data comes from. On the C# side, it is one model. Models don't need to match table definitions. They can match whatever data structure you return.
I hated tuples mainly because I never understood the use case for them (last time I heard about these was in python when I was a beginner) this explanation actually makes sense and seem like they’ll be enjoyable to use in comparison to a class
I'm glad this cleared things up.
Really good as always 👍👍👍
Thank you!
What is the name of the feature you have active showing the parameter name in grey in the called methods parentheses? Not seen that before but looks useful
It is a parameter tooltip. Here's a quick video showing you how to set it up: ua-cam.com/video/morBKqtqmso/v-deo.html
@@IAmTimCorey nice one Tim - sending thanks from the UK i've learnt so much from your videos
Unless I use it in a program I won't truly understand them. It's funny I am trying to learn just the WASM side of Blazor now and possibly add connections to an API and I don't know yet if this would benefit me. I can't wait to understand blazor more, and I am trying to make it my goto so I can help others convert to blazor wasm :)
Tim, I have an api. I have to return at minimum 2, but potentially upwards of 4 to 5 lists to a front end app. Lists range from 500 to 10k values. (If that even matters here) is using lists as the retuned types instead of string and bool good practice/safe/sound.. etc ?
This doesn't sound like a case for a Tuple. It sounds more like a case for a List of lists. Since you don't know how many lists will be returned, you need a dynamic way to return the sets. That seems more like a list to me. The only other thing I wonder about is the fact that one of those lists can have up to 10,000 values. That seems like a lot to return in one call. Usually, an API will page that data since then the client can get the data faster and the client can also determine if it needs all of the data or just the first few sets of data. Just a thought. It might not apply to your specific situation.
@@IAmTimCorey Thank you for the clarity, Tim. My safety net as far as knowledge goes has been completely broken in this project. I often feel like I'm just hanging on. In this instance, the volume of values coming back is being used for graphing.. specifically ploty.js. Unsure if I'll need to institute paging for this. Another potential step in this long list of unknown steps. As always your help is so appreciate.. P.S. I bought your Dependency Injection course a few months back and slowly working through it. Awesome help.
I hate tuples (though I have used them a few times, lol), but you're showing me some nice tricks I didn't know about. Cool!
Glad I could make them a bit better for you.
Hi Tim..Just a small query. Can .Net 6 library (Dll) be referenced in Xamarin Project.
I had to use a .net standard class library project type in my project.
No, you need a .NET Standard project type for Xamarin.
@@IAmTimCorey thanx Tim
nice! Thanks Tim.
You are welcome.
Hi Tim. You missed to show that you don't need to name the touple elements on the Method end if you use deconstructor (which gives them name) and that you can use deconstructor with var (keeping types from Method declaration)
Otherwise, keep up the good work
I showed that off here: ua-cam.com/video/OI1yzwsyR7M/v-deo.html
I would love to see a video about the « params » keyword in a function’s parameters
I will add it to the list. Thanks for the suggestion.
Tuples are awesome! You introduced me to it from a previous UA-cam video of yours. I use it wherever it makes sense. One use I use it for is to separate a list to save for insert and for update.
The method has generics and a delegate also which I also learned from you. Thank you so much.
public interface ISaveListSeparator
{
(List listForInsert, List listForUpdate) GetLists(Delegates.AreKeysEqual areKeysEqual,
List listToSeparate,
List listToCompareTo);
}
I am glad you are finding them helpful.
Great video format
Thanks!
Awesome content, thanks heaps 👍.
You are welcome.
They been here for a while not the named tuple like the version. very convenient way to send multiple values back
They are convenient.
Very useful. Thanks. 🙂
You are welcome.
Question: Is there any way to detect if one of your outputs is assigned to a discard? Was thinking about one of the other comments and presumably at some point everything is lowered to an out parameter, so is there any way to detect if one of those is a discard?
I'm wondering if it is possible to write a function that doesn't even build objects that are going to be discarded, or even possibly overload a function with discard inputs so it is detected at compile time.
For example, lets say a function searches a string to finds all the words that precede "Tim" and all the words that follow it. It might return a pair of lists. If a user only cares about the words preceding "Tim" then the function wouldn't need to build the second list.
Iterating twice through is fine if the test is whether the word is "Tim", but if the test is more complicated we might want to only iterate through once if somebody wants both. Creating separate functions for each case is probably fine with just two, but what about if the function is returning the 10 lists of words surrounding it? Setting flags could work, but I'm wondering if there is a cleaner way. If this function ends up nested deep in a tree I don't want to be manually passing metadata about parameters around.
No, because that would cause a two-way dependency. The method should not know about the caller. If you want to build something like that, overload the method with various options so that you can return just what is needed.
@@IAmTimCorey So short answer is No. Thanks for answering. That sucks.
I still can't see a specific dependency issue though. Unless the whole tuple is pushed to the heap we should be able to leverage smart type inference with it. If tuples with discards are regarded as distinct types based on their discards then we can regard functions that return tuples as generic over this set. We then treat it like any other generic, or manually overload based on the return type.
It might be hard to implement, but I don't see a two way dependency. We can tune the functions and select as appropriate by hand for use cases that don't leave the stack, so the compiler should be able to manage it too.
I just want to be able to write generic code that looks like it dumps a whole chunk of information bit is actually automatically pruned based on what the caller actually asks for. Is that too much to ask :D.
Thank you!
You are welcome.
this tip really useful , thanks a lot! but I still have a question about the code performance , if I write a method that needs a List as parameter , and this method contains a loop, like "for()" or "foreach" and in each loop I will do something about every tuple item , so the question is weather the code will run faster if I use a class or struct instead of tuple?or I just stick on tuple not to worry too much about performance. Because I had heard about that tuple will decrease the speed of code (but I still not confirm that).
Tuples are still treated as classes, so will be allocated on the heap and garbage collected the same/very similar to the class you would’ve created for the example in the video, except there may be more overhead as the tuples are allocated in sequential memory as they are also treated as arrays would be. I think the main use is just to reduce loc and unnecessary classes, they’re comparable to anonymous types in that sense. Hopefully this helps :)
Sorry my bad I looked into this a bit more and this version of tuple is actually a value type so would be handled differently. My comment above is for good old fashioned tuples if that’s ever useful to anyone :)
I'm not sure performance will be an issue here, but clarity will be. I would recommend creating a class for your data instead of a tuple in this instance.
Can you have a Task of Tuples?
Yep.
Use a tryparse pattern instead imo (unless your function is async)
Why? That adds overhead and reduces readability for no discernible benefit.
@@IAmTimCorey it doesn't though. With tuples you end up with a function returning n number of values into a variable with no definition. With tryparse you can return a single bool value and the value itself. Example: IsCacheValid(out var cacheData). So you can have control flow and data which is only pertinent to a positive outcome.
I don't understand the "variables with no definition". I think you mean that tuples aren't named. But let's compare the two:
bool isValidCache = ValidateCache(out string cacheData);
(bool isValidCache, string cacheData) = ValidateCache();
What's the difference when it comes to definition? You have to define both either way. It is just in the second way, parameters always go in and return values are what comes out. In the first way, some parameters can come in while others go out plus you have the return value as well.
wow..thank you Sir;
...
You are welcome.
Oh, I'm wondering that you're not having a function static void Main(string[] args). But your code still works, why is that?
That's a new feature of .NET 6 where everything is implied in Program.cs. I'm going to cover that more in an upcoming video.
@@IAmTimCorey Thank you
Very good video! However I think it would've been nice to show that you can also assign tuple return values to existing variables in your code.
This is useful especially with the new C# 10 feature which allows you to deconstruct to both an existing variable and a new variable.
Example:
string address = "123 sesame st";
(address, bool isValid) = ValidateAddress(address);
I covered that in this video: ua-cam.com/video/OI1yzwsyR7M/v-deo.html
Should the return variable names be capitalized? I think of them like a class and those are public properties, so I capitalize them. What is the standard convention?
They are still fields though.
Think of them like parameters in reverse. We use camelCase for parameters going in and tuple values going out.
@@IAmTimCorey I did ask around at work as well, and the convention does seem to be camelCase. Though, I still think of them as like a mini class or struct with public props. Which is kinda how you described them in the video right?
I like this format. Great video!
Thanks!
Could you do a 10 minutes or less on Datatables?
I will add it to the list. Thanks for the suggestion.
I couldn't help but comment. The proper version of that address is "123 SESAME ST". It's clear you are unware of the Coding Accuracy Support System (CASS) and how it works. Yes I'm being pedantic, I'm sorry.
As a backend developer I've started making heavy use of tuples, they are particularly handy when developing APIs and you want not only to return data, but business validations and other outputs that might have an impact on the data itself to the calling layers... I've done and seen this done in many different ways along the years, out parameters, reference objects, wrapper objects, custom exceptions handled on the middlewares or other filters... But with tuples the code is so much cleaner as most of my data methods now return both data and response status with that data that can have business rules or other instructions that contextualise information to the calling layers.
The main use case for me Is when calling my domain layer from the controllers along with the data to return, there's a response object and that response object allows me to know if any issues occurred in the domain layer in a standardized implementation. I essentially reduced pretty much all my controllers to 2 lines of code... Get data, and return parsed response object and formated data according to business rules.
I am glad you have been finding them useful.
Hey Tim, Please open the monthly pass.
If you are on the mailing list, you will be informed when it opens next.
Hello, Cython
Hello Tim. Thanks for the video. If I create a 2-Tuple method and want to pass that 2-Tuple as a parameter in another method, what does the method signature need to be? Also, with that same scenario, what if I wanted only cared about one of the returns from the tuple (using the discard char)? How would that method signature be? Thank you
Ideally you wouldn't be passing Tuples around like this too much (otherwise, create a class), but it can be done. If your tuple is two ints then your method signature might look like this:
private void CallMe((int a, int b) myTuple)
If you only cared about one of the Tuple values in the method you wanted to call, then don't pass in a Tuple, pass in just a value.
In my world a Tuple is an object🤔
Not sure what you mean. Do you mean that Tuple inherits from object? Because yes, that's true.
@@IAmTimCorey Maybe i was a bit general in the way i expressed myself. I saw the attribute syntax and went. Aha! that i know. :)
C# is starting to look like python...
Languages tend to look like each other over time. That’s normal.