The Most Confusing C# 12 Feature Yet
Vložit
- čas přidán 30. 04. 2023
- Check out my courses: dometrain.com and use code LAUNCH for 15% OFF any course
Become a Patreon and get source code access: / nickchapsas
Hello, everybody, I'm Nick, and in this video, I will introduce you to a brand new C# 12 feature called Alias any type. It's a really cool feature that can get really confusing if you are not careful.
Workshops: dometrain.com/author/nick-cha...
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
I think it will be really usefull for complex types such as "ConcurrentDictionary" or even dictionary of list or of array and so on...
And of course for tuples with complex types! 👍
Oh that's a good point. I've always wanted to trim those long ass declarations down without having to create a custom class for it.
that was already possible with the old version of using = ...
i saw it being used for this purpose
I think it's simply more useful to let you reuse existing types but under a different name. This will let you express the semantics of what you are using that type for in the type name itself, which can also let you simplify what you actually name the variables. This update to the feature just opens that ability up to more types.
My personal wish for using, much like typedef in C++, would be the ability to restrict extension methods onto them.
Imagine a "using PathString = string" not only to clarify that a string passed to a function is meant to be a path but also being able to extend the class with extension methods without applying those methods to the whole string class.
Sounds like you want to implement an implicit operator on the string class ;p
create a class with a `string Value` property and an implicit string conversion and slap your extension methods on it. it will result in a pretty seamless syntax for the consumer, with the only caveat that you would have to use `.Value` in your method.
@@Kitulous Yes, there are ways to do work this out.
But I'm not really looking for a class to solve my issues with a potential overhead of creating and garbage collecting the class when all I want is just to say "this string be a path" and maybe slap some extension methods on it.
@@JackTheSpades since starting some rust work and looking back at c# it is a bit worrying when solutions seem to be wrap it in another class and let GC deal with it when your just wanting clarity on a type
@@cchance it can be wrapped in a (record) struct, which is a value type.
I'm writing a lot of C# functional programing these days and this would help me SO MUCH. The idea that I don't have to create a DTO just to use a collection of 2 or 3 types is amazing. Next step, make tuples convert to function arguments.
It's nice that they are completing an incomplete feature. Though I'm more looking forward to extension types (previously roles) for proper aliases.
Interesting video.
Note that you could actually do `using a = System.Nullable;` before, but `int?` obviously is a lot nicer. And there are a bunch of things you definitely not do before e.g. named tuples and pointers.
I've primarily used this aliasing in the past to #if types on different platforms etc., which decreases the amount of code you have to have different. Glad it's being extended to be more useful, the addition recently of allowing it to be global was great for me.
This look like the DEFINE in C. It's better than define because it don't let you doing weird things, but it still have to be carefully used.
This is typedef in C and using keyword with types in C++
@@vanjazed7021 Oh, I am too old !
Totally agreeing with you every time you stutter, look at the camera, and go "... weird." Most of the possibilities are just downright strange
This feature is very similar to what F# has had for a long time; for instance, `type OrganizationId = Guid` or `type OrganizationName = string`.
It's super-useful to help you define domain-specific type aliases, which will make your code more comprehensible and more clear that "this field/property is not just a string, it's an OrganizationName". While you'll still be able to just stick any string in there, it's a super-helpful indication to the programmer of what belongs there.
It seems weird when you're not used to it, but once you start using it - and you should for domain-specific types - it feels really natural and helpful.
You mean proper naming? If the property string OrganizationName isn't clear, then I'm not sure what is. Also, this using is only available locally unless you make it a global using, which is very bad practice.
Also, this won't be available outside the assembly.
typescript probably has better example
Same for Rust, and Rust allows type aliases to be generic
For me, this feature covers two important scenarios, though they might be considered essentially the same. The first is the use of tuples locally. Being able to alias the tuple rather than create a class, record or struct, is useful. The second is for shortening names and/or creating meaningful names. For instance, using generics results in much lengthier code. Begging able to give a meaningful name to a generic shortens the code and enhances readability, if done carefully.
What we want is an Alias to a string that will be used for example BookId and will protect us from accidently assigning BookId to PageId which is also an alias. So Type Protection can be enhanced with Alias Protection however the language is taking us to a different anonymous direction
Couldn't do you do by creating structs of strings?
Are you talking about Micro Types?
I used the alias on global level once because i had to use a monstrocity of generics and it just made more sense to give it an alias instad of typing it out. (I couldn't use var because it often was a class level declaration)
I have been waiting for the types synonyms for many years.
So that you can distinguish between DirtyId in requests from an external system and Id from the database. So that one validator accepts string or int , returns ITIN, and the whole system uses only the second type. And so that when compiled it turns into standard types.
This will allow you to better separate the boundaries of subsystems that work with dirty input parameters from the logic of the subsystem that works with validated data.
I hope that the "Alias" property will be added to the reflection.
I actually did the thing you’re showing with the tuple in current C#, just using ordinary class/record/whatever and using implicit casts - it can save so much writing when declaring loads of those values
I like that they let you do this now, although I don't think I will use this feature much. What I wish would be implemented on top of this is the ability to define an alias for generic type constraints, so something like:
using TEquatable = T where T : IEquatable
This is a bad example, but I'm sure at least SOME of y'all got into a situation where this would have saved a lot of time.
Waiting for genetics support and exportable aliases (typedefs, finer score control compared to file and global).
Glad this is at least a step in the same direction.
If it supports checking the aliases as 'typed' parameters it would be useful as a way of ensuring parameters are correctly applied and ordered, e.g. `FetchInfo(Guid companyId, Guid userId)` could be `FetchInfo(CompanyGUID companyId, UserGUID userId)`
This is more usefull for complex Dictionary, of Func definitions
Love this. Great for intermediate LINQ types that need concise names to convey meaning. Don't want to get my hopes up, but this pushes C# closer to structural typing like TypeScript. Could see expanding the using keyword to do duck typing - identify the *shape* of what you are looking for (not just constructors and type definitions, but methods and members, too.
Yeah, shapes (and extensions for them) have been considered for a while now, but nothing concrete to show for it, still.
I could possibly see a use if it could provide strict value typing. Example: using Temperature double; void AddT(Temperature t){ //code} ; double t1=10; AddT(t1) ;//fails ; Temperature t2=20; AddT(t2) ; //succeeds because you can only pass the same alias - thus ensuring you can only pass a Temperature into the method, even though underlying, it is just a double. But frankly, I moved to C# to get away from the ability to let a language do elaborately confusing things. So this feature needs to go back to the drawing board.
But don't you end up with weird casting all over your code. You can't assign double to Temperature.
Temperature temp = new Temperature(2.0);
temp += new Temperature(1.0);
Can you give me an example where this sort of thing is useful and can't be resolved by using a readonly or private setter?
Well the first thing i would do is this for math/simulation/game related stuff:
using U8 = byte;
using S8 = sbyte;
using U16 = ushort;
using S16 = short;
using U32 = uint;
using S32 = int;
using U64 = ulong;
using S64 = long;
using F32 = float;
using F64 = double;
Much less typing and i see th number of bits use immediatly.
I could see a few places where this would make tuples easier to define for internal code use. I agree that it could get complicated with naming if the team working on it hasn't come up with a good guide to follow with naming.
I would absolutely use this for some of the situations with nested generics to clean up some of the code and make it simpler to maintain.
I have some of this going on in Velaptor and this would really help clean up and simplify some of my MOQ setups with my unit testing code.
As for making something like this public in an API in a library, I don't think I would ever do that do to the threat of confusing people.
This would be great for some complex LINQ constructions, especially with nested groupings, where you can't always depend on type detection for a var variable, but don't want to have to keep writing out a multi-line type definition.
I like the idea, so for some scenarios this helps the usage of tuples without necessarily needing a class/struct... but I'm still feeling it weird because my usings will be internal knowledge of my project, if I expose this project as a library, other projects will be enforced to use the tuples or create usings as well... nah, skip.
A feature that's been missing.
You can now define your own data types. Something Pascal has been able to do since the 70'ties.
type
TSystemId = integer,
Vs.
global using SystemId = int.
It still only works within one C# project though, right? (I wonder if someday they will let us expose these as public types. Not sure that would be a good idea.)
@@Jared-150 What about if you create a library with a global using and reference that as a nuget package?
@@temp50 I doubt that will work. (Haven't tried.)
The other noticeable difference is the tuple declares its members only as fields, but a record struct uses properties for inline declaration.
Love the numbers you chose Nick. 6,9 and 4,2,0.
I like this feature. loggers, timers for debugging, settings classes etc. Very handy
Just replace the using keyword with the type keyword and let us give them visibility modifiers and I’d love it. Bring the cool stuff from typescript into c# please😊
I've definitely had use cases where I would have wanted to use this, specifically at times where I am having to quickly write an integration between domains and thus need ad-hoc data structures to combine things that I would use ValueTuples for. However, if those ValueTuples then get reused in several places, not being able to instantly refactor the type definition across a whole a project would be frustrating. Despite wanting the ease of the `(x, y)` kind of syntax for this purpose, I'd revert to using explicit record structs purely to workaround the incompleteness of this feature. The priority in these cases is usually to write code quickly, not necessarily to have long-term maintainable code (where an explicit class/record may have been preferred).
This'll actually be very nice for interop with native C libraries that have their own special types. Like how OpenGL has GLuint
On one hand, I like that it means I don't have to explicitly make every record want to use.
And I guess it would make assembly scanning with like scrutor cleaner etc...
On the other hand, I can easly see why me not explicitly creating my records is going to bite me in the but down the road.
(Like other people copy pasting my code, also using the same using statement, modifying it and then having it look the same but not be the same :D)
If a new naming convention is applied, then it would be easy to identify when used. Another possibility is that the IDE can identify what it is in the tooltip when hovering over the variable.
This could be interesting for long "custom" types, especially if you make it global.
As example: instead of writing SomeWierdClass everywhere, you could do:
global using ValidateTypeX = SomeWierdClass;
And then can use that everywhere. Especially if it's getting extended, you just have to change it in one place.
This is very useful when you're in generics-hell, like when using OneOf.
With the previous solution, you could have a type alias for example for System.Int32 and then use that type alias in your code. I imagine that would be usefull for when you don't know the final type yet, and maybe later want to change it to Int64.
Now, using type aliases for tuples instead of just defining new structs seems kind of odd to me, as I don't see how that would really improve maintainability or performance of the code, at best it saves you a bit of typing but at the cost of confusion. Also record structs exist, which is not much more typing than tuples.
What I'd like to see however would be generic type aliases:
using StringTo = Dictionary;
Naming touple items is not new at all (it was introduced with valuetouples in c# 7, if I am not mistaken). I have it used this quite a lot. The main uscase for me is returning structured result from a method/local function. You don't need to define a class or structure, but it is not an anonymous class either, which can't be returned directly. And much less complex than dynamics. I find it great to have now the possibility to "typedef" them.
What about generics becoming part of the alias? Something akin to ```using Lookup = Dictionary;```
What about using alias for the decorator design pattern? ```using Window = ResizableWindowDecorator```
I like the numbers you choosed more than the feature itself! 😅😂
he always uses 69 and 420
This issue was mitigated through the use of BCL types instead of aliases.
Examples:
using MyInt = System.Int32;
using DbInt = System.Nullable
using Point2d = System.ValueTuple
How can you have named values on ValueTuples with the BCL type version?
@Nick Chapsas Well, I did say "mitigated", not "solved"
The main use case of the extension of aliases is generic type reduction. Say for example you can simply define "TypeDictionary" which would be expanded to "Dictionary", that would be actually useful. I'm generally *always* against using tuples; named types are always more preferrable. Anonymous types cover the cases of not wanting to manually type out all the types, but exposing something vague like a tuple, even when its properties are explicitly named, can lead to confusion. Only deconstruction is useful from tuples, which can still be proven problematic in certain cases because the only mapping is the order of the variables; it only really works in simple structures like 2/3D points and fractions, whose order is concrete in our brains.
Other than that, the global toggle for a using statement has been a double-edge sword since its inception. Very useful, but not exactly customizable, say which to expose to the public surface of the package or not. It's got its nits and seems like a rushed niche-value feature. I wouldn't complain about what this extension could bring as a problem w.r.t. global, as it's a global problem on its own.
The other feature I could be interested in is how it blends with unsafe types, there's some design notes about this issue. The compiler had a bug since the original release of the feature that secretly allowed unsafe types to be used without any requirement of the unsafe keyword. However, they were mentioning the probable inclusion of support for pointer types as being aliased, much like the renowned C++ size_t.
size_t in C# is just UIntPtr or nuint, so it's not unsafe..
You'd need it for something like this:
using MyFuncPtr = delegate* unmanaged[Cdecl]
I like new ability to give proper names to the tuple items; it makes code way more readable.
If this is applicable in xaml this would be quite useful as xaml doesn't support generic typing. So if you want to make a compiled binding you'd have to specify a type that the VisualElement is going to receive. If the type is for example List you cannot do that in xaml. A work around was to create type that inherent from List and then let VS to implement thr constructors so something like
StringList : List
Basically a weaker typedef. It would be nice if they support generics like
using StringDict = Dictionary;
It would be better if it can be put inside a class, so other people can use it. For example:
public class MyClass {
public using MyNumber = int;
}
// in another assembly
MyClass.MyNumber n = 0;
I actually had that point originally in the video but I cut it out because it was confirmed that this isn't coming on Twitter
😢 always waiting for a proper typedef in C#
I can see the basic "using" being used in interfaces with verbose class naming e.g. from xsds.
This looks both good and bad. Good because simple tuples aliases are created which are somewhat streamlined by the given name. BUT it's bad if it's meant to be universally used and passed to external callers. In that case you'd just get a Tuple instead of Coordinate. In that case just create a class/struct for it. Using global using would just further continue the atrocity.
If it's something varying, just use tuples normally where you can see their types. If it's consistent, create a struct/class for it.
The feature looks like an invitation for writing bad code.
It would have been interesting to show the IL for the Point2D from the using. Did it create a record underneath? How is it logically grouping the X and Y together.
I love it. I will so abuse this in combination with extensions :D
Can you have generic parameters in the using alias definition?
Are these aliases opaque? Do they prevent BookId bookId = '123' from been assigned to AuthorId authorId = bookId if are both definded as using BookId = string and using AuthorId = string?
6:20 when I was using Unity I wanted that syntax for points so much!
Does Unity not support records?
I think its a neat way to reuse named tupples or to simplify large names if you need to write them a lot in file or globally. But i wont be using the "new" keyword in any of the cases doesnt make much sense. Btw does that convert/parse?
That looks hilarious! I will difinetly use this...
Hi Nick, how about a basic video about best practises for return-values in APIs? Should I return my list of values directly, should I wrap them in a ActionResult or are there better practises?
I quite like this and I'll use it, I think. Do we know if we can alias generic types with this? like could I write this:
using IdDictionary = System.Collections.Generic.Dictionary;
This tuple thing is very interesting. I wished naming tuple would be easier without defining poco classes if used only inside a class.
Can one alias cast to another, if they have same underlying definition?
When i make my own vector or point classes, i like to make an implicit operator which turns tuples into the class, so that i dont have to use 'new Point'.
Can you make generic using statements, like so?
using Point2D = (T, T);
That would be cool if some of your points need to be int based and others double based!
I use it for special dictionary types which is reused across the app, so I dont have to specify always the type params of the dictionary
What I don't really like about tuples named with using is that it adds another way of creating something we basically already had through records. There are of course a few differences but I think scenarios in which these differences are important call for a custom struct anyway.
I have seen this quite a lot over the years especially in C++ that there are dozens of ways of doing basically the same thing in slightly different ways and each way allows you to use it in a specific corner case which the others don't. But that makes it much harder to learn C++. The complexity of the language bloats completely out of proportion in relation to the usefulness of these details.
Extending the scope of where using can be applied seems like a good idea, but the use case with tuples in particular seem like an unnecessary complication given that records already exist. If one of those two was effectively implemented in terms of the other then it would make more sense because it would just become a shortcut to the same thing. But this seems like it could become a minefield very quickly.
What I would like using to support is something similar to C++ partial template specialization. That feels much more useful than these tuple shenanigans.
I like seeing more C++ features in C# because I like writing C++. However, if anyone has seen the C++ STL, what @NickChapsas says is very valid. It is a soup sandwich, and it can be difficult to read unless you are used to it, not me. This is a cool feature when used responsibly. I understand this can be precompiled out, lowered to something else simpler and more performant... I think this is a dangerous road. Just because the toolchain is built into the platform, this approach does not scale. I think the Javascriptifiers have already shown this.
As a part-time C++ user where typedef is very common, I'd likely use it. Hopefully Unity will support it soon. I have more than a handful of structs in my project that could be replaced by this kind of Tuple. Likely I would make none of them global. Global need is rare enough that it always makes sense to define a proper struct which also allows to do some value validation etc.
This reminds me old joke "define true false happy debugging".
How does it perform between a record struct and a tuple?
WOW. It's looks like that somebody took a decision to have especial feature, that could turn all code into a "can of worms". It's very cool to make aliases based on other aliases, that are based on other aliases 😂. I've just remembered maroses in C++ - killer feature, that could kill anybody 😂
These must be regulated heavily in a team. Too many aliases and the code will get very confusing. Should only be used in very specific cases where it actually improves readability by a LOT.
Too bad that it will for sure be in libraries - and you cannot regulate that.
i feel like the traditional creation of traditional structs is just getting forgotten at this point lmao
I will get super controversial and sacreligious here, but this feature could be used for simplified version of dependency registering. For example you could globally alias your MyPrinter class with `global using Printer = MyPrinter`. If you want to use a different printer, you just change your global using. If you want to use a different printer in one place, you override it with local using. I know this isn't as sophisticated as dependency injection containers with configurable object lifetimes, but it's certainly easier to comperhand, as there's no hidden magic, just language features.
I like it.
Like for ex you have player and monster hp, defense, attack etc. and you don't want to use everywhere int, float, double..
Just define it at one place.
But would even like more as someone pointed in the comments, a class based typedef like
class Enemy {public using hp = int;}...
And no, I don't want "value" type ;)
And will be even more helpful for interop with C/C++ Win API for ex, you can def for void ptr, handle, wchar etc at one place with C# types :D
I constantly use Tuple-derived classes for dictionary key because it saves me the GetHashcode and Equals implemenation. But I dont want to deal with ItemX properties so they get named getters retrieving the ItemX vale. This will save me the full implementation.
Why not use record structs? They are immutable as well.
This can give a false sense of security ; using ThisID=int; using ThatId=int; ThisId tid=1; var wrongOutcome = f(tId); object f(ThatId){...}; NOTE: passing a ThisId into a ThatID is "fine" because both are ints, but everywhere else this would be a type safety error.
I can see it used for simple rules for those who aren't using a rules engine when writing switch assignments. var value = rule switch { ... };
Keep in mind, if you have a ton of rules, this would be an inefficient use; write a rules engine.
Nice one buddy.
Love this. It's mighty useful in large projects. However, adding it to global is asking for trouble, lol
I understand why they extended it, which is most likely in preparation for extensions and discriminated unions, but the whole use case for aliasing types like tuples seems kind of pointless. Especially when you can achieve exactly the same short hand notation when using things like record structs.
What is the memory usage and speed of initializing this new using type compared to structs and records and C#
Since it’s just an alias that gets replaced with the actual type in compile time, it’s exactly the same
I return tuples when i am lazzy to create class or récord and i return múltiples values in only one method
I have missed typedef, and this is pretty much that. So, cool. 👍
Why benefits does this have over making a class or record for point2d and point 3d?
Good question. I only see downsides.
I love this Feature
Is it only an alias or is it considered a new type?
What does typeof(Point2D) return?
Looks interesting, but yea, i'd still just rather create a new class for doing this for the sake of longevity. I can see it working best in personal projects where you just want to do some tests for example and want to do it as quickly as possible. But can't see any use-case for release code. Maybe when you are not allowed to create new classes for some reason as a project policy? Or in those programming races when you have to do a certain program as fast as possible, then this could be a neat time-saving feature.
So, C# has finally implemented, essentially, typedef. Well that only took 20+ frickin' years!
I've been obsessing about this for literally decades. Now what am I going to complain about?
*Benefits:*
This improves code clarity and intelligibility by having consistent descriptive type names.
The codes is more in the domain language rather than the C# language specifics.
This eliminates "primitive obsession" - or rather, permits it, and permits getting out of it effortlessly if & when needed.
This provides a greater degree of encapsulation & abstraction.
Was your routine's signature (string, int, int)? Now it's (CustomerCode, JoinYear, MembershipStatusFlag).
And the ease of change it gives your code is amazing.
Suppose you have CustomerId, an int, everywhere in your monster codebase. By some changed business rule or merger or whatever, it now needs to be a long. Change one line and you're done. Ooops, it actually now needs a custom ToString functions, and a validation routine, and some logging functionality. There are currently ways to do that, but what could be easier than just making it into a little struct, and now all the thousands of places it appears in your code base "just work". If you have hundreds of routines passing CustomerId's around as ints, it's a whole other story.
Perhaps that's overselling it. You'll of course want to make sure that there are no bug-creating assumptions about CustomerId currently in the code prior to making that change. Thus before making your desired change, you'll still want to scan through the code and see where and how a change of CustomerId type might cause a problem.
You know what's thorough and efficient to search your code on? "CustomerId".
You know what's neither thorough nor efficient to search your code on? "int".
Finally, and this is maybe mostly just about how I prefer to code, but I like to get in and write the logic immediately rather than hemming and hawing over the details of required datatypes. The datatypes that are sure to evolve over time as I refactor and clarify my own understanding of the code. I hate when I realize a month later that I've locked myself into what I initially assumed should "obviously" be one datatype, only then to realize it should be something else. I also hate (although usually not as much) where, to avoid that problem, I initially over-engineered it to be a class or struct, but now realize that a simple int would've been better. But now, having a kind of global typedef, I can just start writing the code in terms of CustomerId and so postpone worrying about whether it should be an int, or a struct, or a string, or whatever, until later.
*Drawbacks:*
NONE
typedef in C was needed, in C# not so much. Apart from aliasing a type in a complicated namespace and work out the ambiguities I don't see this hitting production code for years to come. Now if they would add generics that would make it more useful.
One of the things that makes C++ confusing is all of the alias types that everyone and their grandma feels like their purpose is to add alias types. I hope libraries don't begin unnecessarily renaming with aliases everywhere
I worked in Delphi for a while and was using this a lot, it's a good remedy to primitive obsession and DRY while controlling global scope type proliferation
The video makes me know C# 12 Preview is available and I have just taken a look at it. And it was a bit disappointing because Microsoft has improved the primary constructor and lambda expression but both are not good as I expected. I hope the guys from Microsoft have tried Kotlin before and they know how cool is it with primary constructors and lambda expression with default parameter "it".
The actual point is we should not ever have struct record. We should just have this feature from the start. And it will not confusing
This is not enough. I want to be able to code something like this:
using aLoLoD = List
I wonder what it looks like when lowered?
It would be better to have improved usings for importing partial namespaces.
For example, when you want to implement feature slicing, using simple names of classes like 'Request', 'Handler', 'Validator', etc, you need to write aliases everytime to distinguish one feature class from another. But with partial namespaces the syntax could be smth like this:
using MyFeatures;
var r1 = new Feature1.Request();
var r2 = new Feature2.Request();
Can we use an alias for the entire namespace in C# 12?
Most confusing why? It appears to be very very very clear.
Awesome video, but why are you using VS and not Rider?
Because the feature is only supported in VS preview atm
hope this feature finally like typescript's type XD
It's something that I'll never use unless it's extremely needed. I'd like to understand all the types when I read the code without checking what is this and from where it comes
i have a good idea replace every record in the production code base with a global touple using and commit it from a shared account wiht the message -m "smal changes"
so like typedef in C/C++ ?
Interesting? Sure... though I'm guessing it's going to be some obscure feature that I'm going to see once in 3 years after I've forgotten this exists and it will make debugging really hard to follow. C# introduces a lot of really amazing features that I love, but very often it feels like they are bloating the language with extremely niche use cases that can cause way more headache than alleviate.