The New Constructor Type Coming in C# 12 is Weird
HTML-код
- Опубликовано: 29 янв 2023
- Enroll to "Cloud Fundamentals: AWS Services for C# Developers" for FREE: bit.ly/3XKUBOH
Become a Patreon and get source code access: / nickchapsas
Hello everybody I'm Nick and in this video I will show you a new feature that will probably be added in C# 12 called Primary Constructors.
The proposal: github.com/dotnet/csharplang/...
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
We’re about two C# versions away from being able to have a certification exam dedicated to object creation and initialization
I think we're already about there. There are so many features now that you have to agree as a development team what features you aren't going to allow.
What can be confusing about a Ref struct value task async record 😂
@@robl39 :D
Aaaargghhh! I guess it's time to go back to BASIC!!
@@robl39 depends on schedular too
In my humble opinion: confusion it creates > benefit we get
I think that most languages get bloated with features that do almost the same but in a new "cool" way.
@@igorthelight ehjm how js classes are just old objects prototypes with another syntax
These new features hide what's going on behind the scenes. Few programmers will know about the implicit code, resulting in less code. I just learned about record class and record struct.
While this seems alright, I feel like doing what Typescript does (automatically creating & assigning fields when you add an access modifier to a constructor parameter) would be more flexible and clear than what they're planning here.
Yeah I prefer that I think
I was just about to comment the same thing. This looks way better to me:
class Person
{
Person(
private string firstname,
private string lastname) {}
}
You could even allow for read-only here and it would still be very readable. Only thing is that it's deviating from Records syntax.
Doesn't work; C# allows multiple constructors while JS does not
@@modernkennnern See that's the entire point of a team working on a language, it can evolve over time🌟
@@modernkennnern Fair point, I didn't consider it.
It doesn't make introducing it impossible, though. You could always impose some restrictions when using this feature. Even the same as the current prototype presented in the video imposes.
So we're basically stuck between the TS syntax which is nice, intuitive and simple but inconsistent with how the records work and the record's syntax which is less intuitive and much different to how constructors look like now, but consistent.
Since I've never been a fan of C# records, I'd still prefer them going the TS route.
I feel the C# team when adding new features have stopped thinking about why they are adding features. A lot of the recent bloat feels like they saw something that was working in different languages, but they didn't stop thinking if this was appropriate for the language. Like what are they trying to solve? primary constructors are basically record syntax for classes, which itself was just adding a new keyword for what they are doing now + a bit of syntactic sugar?
To go to one of my more extreme opinions I would say it even goes back to Auto-Properties which are just syntactic sugar for technically not breaking encapsulation while functionally breaking it and without actually acknowledging why you would or wouldn't encapsulat in the first place...
The core problem imho is that the C# team seems to add features without thinking about what you would want to encourage, and what you wouldn't. Kotlin for example is default non-nullable to discourage null-use, and offers different approaches with the Optional-type for example. What are primary constructors trying to encourage, except for just having an extra-way to write a constructor? Kotlin uses it to decouple actual values from initialization logic (Kotlin encourages not having more than one constructor), but for C# I feel they just put it in because people like it in Kotlin...
Sometimes a language with fewer features (that are more coherent) is better for creating good software...
/edit: Previously I wrongly stated that Kotlin only had one constructor. Thanks Vasiliy for pointing that out!
>Kotlin doesn't actually have more than one constructor
This is incorrect. You can have secondary constructors which delegate to the primary one, or have no primary constructor at all and declare multiple different constructors using "constructor" keyword in the class body. If you meant you can't have more than one primary constructor, well, that should be obvious.
Most of the recent focus has been trying to remove 'boilerplate', code that we write over and over in 90% of use cases. A big chunk of usings at the top of every file, static void Main, and etc are all things that just get in the way of understanding the code, they tell us nothing that we need to know and make it seem more complex than it is. This falls into the same category; the code is much easier to understand and maintain when it's concise and to the point, and this can be used as-is with DI (which a good 90% of modern code uses), which otherwise can easily take up an entire page of nothing-code
If you really prefer it to be verbose, you can write lowered code yourself. If you think lowered code is hard to read or understand, then you see firsthand why syntactic sugar is valuable
@@Dimencia I think you misunderstood. I am not against syntactic sugar at all. I just think that when adding features the considerations should go beyond "this is done often, so lets add a shortcut", but something that is purposeful in encouraging good design.
If you have two features that look similar on the surface, but are different in subtle details that might lead to confusion and actually create problems (like Primary constructors in classes vs the same in records)
If you have features that actually encourage bad design patterns (like auto-properties do by telling you that a field is encapsulated, when in fact it is usable as if it wasn't), that can also create problems.
On the other hand you have stuff like the null-conditional and null-coalescing-operator (?. and ??) which encourage checking for nulls and therefore making the code more safe without creating massive boilerplate.
I also really like the automatic main for short programs, as this was just boilerplate in cases where you would want a 2-3 files-program
My point is that a feature should not just be a shorter way to do stuff, but something to encourage good and clean design. It should remove redundant information without removing necessary context. It shouldn't be easy to confuse but clear up confusion.
My first thought was the confusion with the record semantics. Maybe just extend the record semantics, like adding the field keyword to make it a field instead of a property. For example: public record class Person(field string name, int Age) will have name as a field and Age as a property.
Every time I create a constructor in TypeScript, I wonder why I can't do something similar in C#. I will be very happy if this feature is finalized and it will be included in the final .NET 8 release
In TS this looks like next
constructor(
private myPrivateField: string,
public myPublicField: string
) {
// constructor logic goes here
}
Short answer: usually that's a bad idea to have public fields and you should use properties instead.
@@thecubicnoobik9792
If your fields are readonly it is not really a problem
@@diadetediotedio6918 It's not a problem until it becomes a problem. That's why we have encapsulation.
@@lordmetzgermeister
Encapsulation won't save your code from breaking when it has to break, that doesn't even make sense, when you make a field readonly it's precisely because you're building immutable models, other languages of more functional veins keep the same pattern in relation to that.
@@diadetediotedio6918 Of course it will. What won't save you, on the other hand, is not understanding why we actually have properties.
I like that primary ctors use the same syntax as records, it's clean and simple. I hate that it looks the same but acts differently.
If they unify those two and add all the initialization options (access modifiers, get/set/init, required, readonly,...) it would be quite complex but still more concise than what we currently have.
Could save a couple of lines but on the other hand it could force a programmer to work as such a code-lowering tool (like sharplab) just to figure out what this code _actually_ means. And the main problem is the confusion with records so that exactly the same syntactic construction leads to another result.
I really like this feature for the many cases where you are only using the ctor for dependency injection. It doesn’t mean that you have to use this feature for every ctor from now on. Just don’t use it when it doesn’t make sense or switch to a record.
I really like this
Seems like in some time C# will look something like C++ with all the grammar and 14 ways to init a variable😂
Even the Python which positions itself like "easy and readable" already have so much bloated features... ;-)
Oof...
I think the C# team should be more concerned with improving compiler type inference (it's really terrible compared to something like Kotlin or Rust), and also working on a solid escape analysis system, but it's an interesting feature still so.
I kinda agree and I have even done a suggestion in the csharplang github repo to be able to declare things with var variableName; (without a type behind it) and infer the type based on later assignments, but it got downvoted and rejected.
Though strangely I must also admit that I am actually used to being very explicit in specifying types in C# code as well (this is probably because I am a C# developer since the very beginning of .NET), so it's also something I feel difficulty with to have a very strong opinion about. :)
But currently I am very actively learning Rust too and this influences my ideas a lot (absolutely magnificent programming language), bringing me back to the idea of flexible inference again.
@@jongeduard wanting the compiler to do better type inference in C# and aversion to var sounds very relatable. i experience the same conflict. in F#, type inference is the norm. i haven't tried Rust yet
And if you compare all 3 with C++ they are all in diapers.
@@anthonysteinerv
Yes, yes, I think after much thought I can see, you have... SEGMENTATION FAULT (core dumped)
@@diadetediotedio6918 You know there are exception too in C++? and that you can code without them in both C++ and C#, just the error message is different. UnhandledException or NullPointerException from Java, or any of those Fancy errors, are just that, fancy errors. So weird ass comment. And doesn't change the fact that C++ is years ahead of any other programming language in type inference.
The confusion between classes, structs, records(classes), and record structs is already too much. I like the record idea, but I think having a direction to move towards is important. Sharing syntax but doing different things is a mine field that should be avoided.
I do most of my work in C# and it's hard for me to do something different without a guide/flowchart pulled up and
It's also a minefield to have the required keyword introduced, but not really be usable in many situations because JSON deserialization doesn't set it properly. It seems like they're thinking about adding more to the language without thinking of the long-term. I don't want to insult anyone working on the language; it's really a great language; I just think we need to maybe think of a breaking change that could consolidate all these disparate attempts to make the language more modern.
I liked the Kotlin implementation. It seemed expressive and clear. TypeScript does something similar as well, and after years and years of using DI, I've learned the view the constructor not so much as a place to put logic, but more as a declaration of the dependencies for my class. The primary constructor really seems to cement this philosophy. I like it!
This is becoming crazy to keep up. Record struct and record class already behaves differently. Now this… I can see now how Rust and Go have the right approach “There is only ONE way to create an object”. If you want to use the convention of “NewObject” (Go) or “Object::new()” (Rust), then it is up to you.
Yet even rust has:
MyStruct { field = value }
MyTupleStruct(value)
MyStruct::new(value)
MyStruct::merely_a_convention(value)
my_macro!(value)
my_macro! { value }
Then add on all the verbosity of using generic arguments, sometimes even a :: fish operator.
@@BenjaminBrienen none of what you said was a way to fundamentally construct a struct. Everything you wrote are “helpers”
well, rust’s case goes deeper than that. new is just a function that you may or may not define. you create structs and enums by using their inline constructors and return them. is clear and it’s simple.
@@henrycgs you literally said the same thing as OP
@@_sorashi I might be wrong, but I'm pretty sure OP edited their comment to include that stuff
Why not just follow the Typescript way and put access modifiers in front of constructor parameters to make them fields/properties?
e.g. public class Person { public Person(private readonly string firstName) }
Your example is a little bit complicated. Better to not introduce new syntax at all instead of doing what you want
For me personally, it is a kind of magic I dislike. It would be better to have one-for-all syntax feature like records to fit all these kinds of operations.
Hi Nick,
I hope you talk about Vertical Slice Architecture and compare it with Clean Architecture and when you recommend to use each one of them?
A note on phrasing, in native english we say "*What* it will look like", rather than "How". Though we do sometimes say "How it will look". Confusing, but the what/how issue is quite common in non-native speakers and it sticks out in phrasing. You otherwise would sound pretty native.
I think given that records already do it with differing semantics it probably ought to default to the record behavior and then you can add an access modifier (public/private/protected) in front of the identifier to get the different behaviour.
Or perhaps a different modifier, as one might expect `public` to only change the visibility level, not whether it's a field or a property.
"Back in my day, we only had simple constructors, no separations between primary and secondary, everyone was equal. We wrote all our good old boilerplate and we were happy about it too! Nowadays, young people are fighting needlessly whitespace left and right, as if they don't have the best IDEs and AI autocompletion tools, they don't even write code anymore, they just press Tab and call it a day. Ah, life was so simple. Those were the times." says, rolling in his armchair.
Yeah... I'm not convinced. Sorry.
LMAO
Yep, nowadays we can write the same software, just in 20 different styles.
I really hate this AI code completion. Don't know what the I stands for, but it's not intelligence. Constantly hitting key unnecessary, totally slowing me down
@@seesharp81321 I immediately turned that s*** off
It's definitely going to reduce the amount of boiler plate required to accept (and verify) dependencies, but at the cost of yet another way to define classes/structs. Is it possible to still do manual validation? (i.e. public class Person(readonly string firstName) { if (string.IsNullOrWhiteSpace(firstName)) { .. } } and still have the compiler assign the private field?
Awesome to hear about the AWS course! Love the others you offer
I like the way they implemented similar feature in PHP 8: constructor property promotion. You just pass visibility, type, and property name as contructor parameters and use them as if they were declared in your class: ```__construct(protected string $firstName, protected string $lastName, string $regularParam)```. Remove visibility, and it's now a regular parameter.
New versions of C# come out so fast now I feel like we're being trolled by Microsoft.
Once per year as most other languages.
Personally, I don't think that we NEED all of those changes ;-)
@@igorthelight Agreed.
It's really a confusing feature but when implemented properly it might be helpful like in Kotlin. So if Microsoft is going to do this - please make it once and do it properly
Remember when C# was rigid by design? Compared to the early days it's unrecognizable.
True!
Some of these new features and syntactic sugar are nice, but some of it just makes the language complex and confusing, much to learn, much to remember, "cool" things that are rarely used, and things that I come across that I would not be familiar with, or be easy to grasp. I like records and init setters though. I would rather see something useful like a Result type like in Rust.
The reason I'd like all constructors to use a keyword like "init" - it does not need to be renamed when the class is renamed.
Default behavior is all over the place already regarding readonlyness.
If this is going to be added - it better come with something like "public" and "readonly" specifiers to be able to achieve desired behavior.
Maybe C# should start to introduce the "mutable" keyword so it would be easier to go with readonly by default.
Another problem with this feature (besides the readonly problem) is that there is no way to declare fields as protected
I love the kotlin approach, but F# also has it right -
Use type keyword to define a type, add an atribute to signify that it is a struct, and enable let bindings (init logic) anywhere in the class.
Records use a different syntax to highlight the difference.
That might be a good way to create the same behaviour as records, however only if those two behave the same way. Specifying an access modifier in primary ctor would be nice too, I find it easier to read, especially with a lot of value object and dto classes.
Needs public/private and properties as you said with set/init.
And record and class should work the same.
For how applications work nowadays I like this. An injection constructor is never called (outside of testing). All it does is define the dependencies. And it kind of makes sense to have that in the class declaration header itself
So, now that we're at RC1 for net 8 with C# 12 and this feature is included, what did they settle on for include constructor logic? I can't get any of the methods Nick suggested to work.
I do like the idea, especially with dependency injection. I do think though that stuff like an init block is overcomplicating it a bit. At this point, I'd pick a regular constructor. The reason I started using stuff like records is that I can quickly cobble some classes together and to have them comparable for unit tests.
And, as most others said, maybe the TypeScript route would be better. Allow any access modifiers in the (primary) constructor to make them fields. If you want to add another constructor, you could then be forced to call the primary one or just not allow it.
The big thing I see missing here, param validation.
Unless your class is IValidateableObject, and DI should trigger that upon resolution.
But that's gonna be weird for other instantiation outside of DI.
What I've been wanting are param attributes, think of [FromBody] except more like [NotNullOrWhitespace].
I hate writing boilerplate param validation on ctor and methods.
Some may not know VB has a default constructor syntax, but in C# we have [InjectionConstructor] and I believe we also have serialization ctor attributes.
It used to be code was hard to read because machine language or assembly were not syntactically similar to spoken languages, but at least everything that happened was represented in the code. Now, I feel like it's getting hard to read because one line of code can trigger so many operations that aren't represented in the codebase but that to affect the codebase (like adding fields that aren't visually present). The syntactic sugar is getting so thick, you can't see the shape of the cake. I feel like if you condense too much functionality into a single line of code, while easier to write, it can start to become difficult to maintain. For this particular feature, having the ability to add logic seems to undermine the whole feature because now you've just got your parameterized constructor and you're really not saving anything, you're just scattering the definition around and obscuring the effects of the code. Personally, I would limit the use of this to really trivial cases, otherwise you end up with:
class definition (also maybe constructor parameters) : more class definition stuff here
{
fields (except the ones that are up there in the class def line)
properties
implement the apparently nontrivial constructor whose parameters are up there in the class definition
function that uses fields that aren't actually in the class definition, they're just implicit because of the function parameters tacked on to the end of the class name, because now that's also a constructor definition whose implementation is (maybe) down here.
}
it's kind of a mess if you ask me.
Agree!
Even Python have this feature bloat problem now (and it presents itself as easy and readable).
Any news on how to define the body of a class that uses primary constructors? The initial draft still notes the { // body }, and that does not work, and I tried the potentials in your video here like YourClass { // body } and init { // body } and those don't work either.
C# becoming ES. This kind of short constructor already exists, including such things as private, readonly and so on.
Someone suggested to specify property or field in the proposal about an hour ago. Maybe a follower from here :D
Primary constructors were a C# 6 feature that was scrapped last minute. It's not an ES/TS/JS concept
I was really glad to hear at the end that you also have the same problem with it as me. I was cringing entire video because of this inconsistency.
My immediate thought, when seeing this, was the naming conventions. It was shown in the video that it causes a conflict. Underscore prefixed fields denote private object scope, while parameters are passed without the underscore prefix. Because it just uses the raw parameter name, without prepending an underscore, it makes it read like a local scope variable, rather than an object scope private field. I don't like the fact that this removes that distinction.
A better approach for this would be like:
public class User
{
public User(private readonly string firstName, private readonly string lastName)
{
// Initialization code goes here
}
}
This solves the need for a init keyword and also makes it more explicit about what the code does
A thing like:
public class User
{
public User(property FirstName, readonly property LastName)
{ ... }
}
Could also be done for properties, so taking properties as public-by-default (because there is no reason to not do so, it is a default in the language) you can make them very shorthand
This defeats the purpose of the primary constructor being instantly obvious. It can't move from the class declaration. That's the whole point
@@nickchapsas
I think it's more obvious than the constructor that looks like records but actually creates private fields. You could also make it so that it is only possible to declare these constructors as the first members of a class (generating a compilation error if the opposite is done), but in any case that seems to me the only way to do this without making classes easily confused with records, in addition to being an existing and common thing in TypeScript.
It would be nice if Microsoft could apply the same approach we have in the typescript for constructor vars, where the variable access level can be defined explicitly in the constructor.
I have to agree, the feature needs more thought put into it. Maybe C# 13.
IMO it makes more sense to let records have modifiers (i.e. readonly) instead of whatever this is.
As like a lot of other people in the comments I much prefer typescript's approach. Although I do know that the idea was dismissed mainly because C# supports multiple constructors unlike typescript.
Personally, I'm not really keen on the proposal and probably won't utilize it when it's introduced, especially when you have behavioural differences between records and classes. DI is a pain, but i can live with it.
I’d like it to mirror typescript, I think they got it right. Declare properties in the constructor, and also have initialization code in one constructor.
Anything else would just be confusion
It would be nice to have constructor for services that automatically set all parameters from base ctor (if it single, for instance)
they have to find a way to make clear what the differences are
I was waiting for primary constructors for a long time after playing with Kotlin in the past. Kotlin has a lot of features that would be amazing to use in C# like delegated properties, extension methods and properties with contexts, etc.
c# already has extension methods
@@dipsy506 only extension methods although
I can see the advantagious idea of simplfying constructor based DI code into a class with this pattern but I can't help but think that adding some kind of mapping attribute to inform the compiler of whether the parameter would be [Field] or [Property] would be more flexible? Perhaps akin to the same syntax that minimal API routes adehere to for the handler delegate.....just my two cents.
Quick Google search shows that primary constructors has already been discussed to be implemented even before release of C# 6. Took a really long time.
I recently had an epiphany: functions are just glorified macros. You peel away the layers of abstraction, you find the underlying assembly language/machine code is just macros. It's macros all the way down.
Is there a way to attach attributes to the created fields (or properties in case of records)?
This would be amazing for short and concise serialization classes and DTO objects.
Yes, there is already
record MyRecord(
[field: Attribute1]
[property: Attribute2]
string Anything);
There is but it is kind of ugly/too verbose.
record Person(string FirstName, string LastName, [property: JsonIgnore] int Age);
@@volan4ik. Thank's a lot man, that's perfect!
Has anyone else noticed that you can't actually see the value of the injected services when using primary constructors? When I hover over my private field when it's injected via the constructor I can see what it is, however, when I use a primary constructor the value is always 'null'.
I think being explicit is more important than bekng concise. In this case, a lot is obfuscated imo (specifically the mutability and access of the variables), which seems like a code smell to me.
TypeScript has something similar to this, while you still need to add the arguments to the constructor, you can prefix them with public or private to automatically create properties on the class. Angular templates leverage it for DI as you stated this sort of thing is useful for. Interesting to see something similar coming to C#.
I feel if you need code in your constructor this feature doesn't make as much sense as you're not saving much by omitting the formal constructor declaration. Might as well do it at that point. So I would be OK with a primary constructor not having code behind it. VS should be given a lightbulb action to convert a primary constructor into a formal one for cases where a dev needs to add code.
I think required properties are going to be the better feature here, at least for my uses. That said constructors have their place as well, though I think if I had a constructor with only boilerplate as in your example I would want required properties.
As usually, C# takes feature from other languages. It exists in TS and Kotlin
I would settle for "init" being a keyword we can use to mean "constructor". Just to make ctors more concise and not repeat the full type name, and make it consistent across all classes.
Perhaps
.ctor( string arg )
{
}
I won't use this feature.
I like the records approach, if I just need a collection of values it saves me a lot of work.
But with the normal classes I often have more, there the constructor is rather a minor matter - or the class is too complex.
This feature would be nicer if we had the bangbang operator that was taken out. I have no guarantee that my DI system will be injecting a valid instance, and I almost always want to check for null in my constructor
Yep, I think it's valid to say that it's a bit too confusing in combination with the existing record struct and record class syntax in the language.
I think they need to do something extra with more keywords to make the distinction between this new constructor syntax with the existing record syntax larger.
I also see a problem that we need to use underscores inside the default constructor (which looks bad) if we want to prefix our fields that way, while this naming style has become the convention in many projects now. It does not fit well anymore.
This and records should have been the same feature. Like in Kotlin you can add the "data" keyword and it will turn into a record but the constructor/property semantics are still the same as with normal classes. I like this feature but I don't like the inconsistency it creates.
Worth it just for the dependency injection benefits IMO, but the lack of consistency with records is just asking for people to get confused and make mistakes.
It would be nice to have readonly and something for null check as well (!!)
How do they plan to support private, protected and internal constructors using this syntax, if at all?
First used this in Kotlin, I liked it helped to remove redundant code. Sure there are cases where it might not be worth the lines saved, but still love it.
I also like it in Kotlin, but for another reason: It forced me to acknowledge what is the actual "default" way to create an object. Since Kotlin only encourages one constructor for anything non-standard I would need to create a static function (well, "companion object function"), which forces me to actually name it, which removes a lot of confusion when I have a lot of ways to create an object, and what is actually the "official" way an object works.
for example a "struct Temperature" with 2 static function "FromCelsius" and "FromFahrenheit" and a private value "inKelvin", that is used in the default constructor is a lot more clear, than a constructor that takes two arguments, one of which is an enum used to define what scale the temperature is in. With only having one constructor and all values being saved Kotlin encourages the first pattern.
/edit: Kotlin can have more than one constructor, it is just not as common as in other languages.
I personally like Dart's approach to this, where you write the field yourself, and as a parameter to the constructor, simply reference this.fieldName. In c# it would look like this:
class Person {
private readonly string name;
public Person(this.name) {}
}
var john = new Person("John Smith");
This seems way more flexible and readable to me while eliminating the extra assignment line.
I can understand the thinking behind this feature - assigning properties directly from parameters passed into the constructor is a big part of what you do in constructors. However, I'm not sure that getting rid of the property declarations all together is such a good idea as it can lose transparency and flexibility. It really does look like they're trying to use ideas from records and implementing them for classes but for me at least, there's a reason why classes are the way they are.
It's a bit much. The naming of init {} further confuses things because in some classes there can be an Initialize() method which will be called later some time after construction.
Still no „extension everything“ 😭
I see a lot of comments mentioning typescript constructors, but I think that would cause more confusion, since different constructors could create different properties.
It honestly looks more simple than calling it over and over, a javascript perception. I have yet to touch c#, but this looks promising.
I and many of the community have argued against realizing the record constructor arguments as public properties. Now the fabulous dotnet team, proposes the very same feature, in addition to the record constructor which is the very same syntax. In German we say "die ham doch wohl den Arsch offen". But that would be to impolite to write in a discussion. So I'll just leave my anger here
What if I want my fields protected or internal, I can put this in as arguments too? I am often optimistic but this just feels like a mistake, especially when conflicting with the record implementation.
protected (and public) fields are not recommended in C#.
Idk why not just modify record at that point so it uses fields instead of properties and just have the ability to set private/protected/public in the field deceleration.
Seems rather weird to have 2 different ways for such a minor thing.
If this new constructor syntax is designed to solve a problem with DI constructors (which is a problem) then its addressing the symptom and not the cause. Its like swallowing a spider because you swallowed a fly. MS should be looking at compiler tweaks to make DI less of a dog, and perhaps even making compiler features to inject (or mock) function calls so DI is not needed in the first place!
For consistency and because the backing fields are hidden from view, they need to be private and mutable. I like the init { } keyword best of the options given.
Just my 2 cents.
Unfortunately, this doesn't solve one of my common uses cases: creating classes that will go through JSON conversion. Those require either a constructor with no parameters or some custom code. However, I like it when I can use a constructor with all the necessary parameters since calling that is more concise than initializing each property. In other words, I need this feature to be able to generate two constructors.
I would prefer to maintain the ability to add as many modifiers as I normally would be able to use. Additionally, as mentioned by at least one commenter below, I'd prefer this to create properties instead of fields by default to be at least mostly consistent with records, and the field keyword can switch it back. At least the new syntax would mean that the adding field keyword wouldn't break old code.
One correction regarding the Kotlin part: You said at around 7:00 that firstName and lastName in your example were fields. That is incorrect - there is no way to directly declare fields in Kotlin. firstName and lastName in the example were simply constructor parameters you could've used in an init block. (To be honest, I wasn't aware a primary constructor could have parameters defined like that prior to this video. I'm also not sure if I like that it is possible)
Hey Nick, can you make a video about Fody, the weaving framework? Thanks o/
Definitely wouldn't mind adding some Kotlin language features over to C#.
I hope this means interface delegate implementations might be on the horizon as well.
I wish it was just the record syntax but for classes. Big difference would be that class properties are mutable, whereas record are immutable. I just want to eliminate POCO boilerplate.
I'm studying English right now and it's a bit hard to understand everything you say, but I can say that your videos are always a great place for me to catch up on all the new features of this beautiful programming language.
I don't have time to read all the docs, or all the code in the repository, and your videos always help me a lot to get up to speed, and to know ll what i need to review.
Thanks!
I'd like to build on the work record has already done. Public mutable properties by default, but with the ability to add private/protected/internal readonly to the declarations. I tend to use get only properties for DI instead of readonly fields anyway. If you need init logic, make a regular constructor.
...or nothing. Not sure I love this feature.
it just doesn't feel good at the moment. I agree with the entire video, it will only bring more confusion to an already large confusing list of ways to create an object, and it's a limited feature. The syntax of primary constructors should simply allow you to put AUTO-property (no custom accessor bodies) and field declaractions there *in their full form*, that is class C(public readonly int i, j, k; /*public by default*/ ISomething { get; init; }). But that would make people split the "parameter" list into multiple lines, at least one per a member declaration. That's why imo the record-like syntax for classes should be scrapped and the feature should be a specially annotated block in the class body: init { /*property (not restricted to auto-props) and field declaractions, or in other words any declaraction that can also be made a parameter; the order of declaractions matters, member names are converted to camel case parameter names*/ TypeName { /*this is a constructor, so here goes a list of statements*/ } }
What about the way Typescript allows defining fields -
in the constructor function you define the parameters as how they should behave -
public constructor (private name: string, private surname: string) {} // This will create private name and surname fields.
Didn't see such love for Typescript in the comments 🙃
My first thought would be if it can handle params ?
Great feature. They only thing that bothers me is the subtle difference between the public properties of records vs the private properties of classes. This distinction will confuse people and lead to mistakes and possibly bad practices.
I can kinda see it useful if I only do dependency injection in a constructor, but then again, I’d also like to have null checks.
C# is starting to look like swift and kotlin. I agree this change could cause a lot of confusion. I'm sure Linus Torvalds could give us a rant on why he hates all of this syntactic sugar 😅😅😅
The ability to make the properties in the primary constructor publicly exposed would create a paradox when it comes to naming standards.
It's nice to have :D
I would prefer to have a constructor that works similarly to the one in Dart:
class Car {
String make;
String model;
String yearMade;
bool hasABS;
Car(this.make, this.model, this.yearMade, this.hasABS);
}
This way you don't mix constructor with the line responsible for class declaration.
Also, current proposal will be looking ugly if you add 5 (or more) variables and implement two or more interfaces and maybe add some generic parameters as well.
I would like something like
TestClass(public / private /or readonly MyProperty)
With the init{} variant
People should be wary about adding too many new syntaxes to a mature language like C#, all it does is add complexity and heighten the learning curve. Plus, with this particular feature, it seems to go against the way people write classes - don't add a bunch of named private fields to a class, only to have them exposed later as properties - just use auto-properties. I get the argument about dependency injection, and sure, it would reduce the amount of code you need to write in that specific situation... but it still seems like the wrong way to go about it.
Thank you for the presentation. I think it is quite messy.
As long as it does not break what I do so far, I am not against that new feature though.
more confusion.
would be nice if at one point Microsoft decided to start from scratch with a trimmed down language based on c#. One where there is only one way to do something, not 10. They can pick only the modern features
I think injectable properties is the way to go for DI most of our constructors are getting bloated with DI injection
Can’t wait for people to have 7 line class declarations because it’s “less code”.
Hmm i dont like it very much. The reason for this is simple: I really hate when compilers automatically modifys or generate codes - making it unnessecary hard to follow the control flow.
This is especially true when you use attributes that are designed to do this -> POCO ViewModel or automatic dependency injectors for example.
But if this feature will come and i need to deal with that - then i would want to have at least control of the visibility like this:
class User(string firstName, string lastName) -> private by default, fields only
class User(private string firstName, private string lastName) -> same as the first one
class User(readonly string firstName, readonly string lastName) -> private readonly fields
class User(public string firstName, public string lastName) -> public properties -> CamelCase
class User(internal string firstName, internal string lastName) -> internal properties -> CamelCase
The same for structs and for records - it should be consistent.