The C# 12 Feature You Shouldn’t Use, Yet

Поделиться
HTML-код
  • Опубликовано: 26 сен 2024
  • Use code BLACKFRIDAY23 and get 40% off any course and 20% off any bundle on Dometrain: dometrain.com/...
    Become a Patreon and get special perks: / nickchapsas
    Hello everybody, I'm Nick, and in this video, I will show you the C# 12 feature that I wouldn't use for a specific use-case that is being pushed a lot by IDEs and Microsoft for no good reason. In fact, using it can harm your code.
    Workshops: bit.ly/nickwor...
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: github.com/Elf...
    Follow me on Twitter: / nickchapsas
    Connect on LinkedIn: / nick-chapsas
    Keep coding merch: keepcoding.shop
    #csharp #dotnet #dotnetconf

Комментарии • 313

  • @deredware9442
    @deredware9442 10 месяцев назад +138

    I think these parameters should just be private readonly always. If you want something public and/or mutable, create a field.

    • @KerchumA222
      @KerchumA222 10 месяцев назад +28

      defaulting to readonly makes a lot of sense for a constructor parameter I think

    • @Saleca
      @Saleca 10 месяцев назад +1

      This seems like the obvious solution, i commented the same, should start reading the top comments first... 😅

    • @zabustifu
      @zabustifu 10 месяцев назад

      But now that they've released this half-baked feature as it is, it's unlikely that they'll introduce such a breaking change.

    • @jongeduard
      @jongeduard 10 месяцев назад +2

      In the Rust programming language everything is immutable by default. I mean all variables, parameters and even global static fields.
      You have to use the mut keyword to make things mutable. Which is much better design than most languages, where you have to do the opposite by using something like a const or readonly keyword for immutability.
      But Rust has the benefit that it's a modern language which was well made design from the ground up, inspired by the many already existing languages.
      Existing languages always have to obey compatibility. This also includes C#. Suddenly making variables or parameters readonly by default would break every existing C# program. So that will never happen.

    • @shiuido359
      @shiuido359 10 месяцев назад +1

      It's a case of "wrong tool for the job". If you want an immutable container for data, just use a record. If you want to quickly create a class with public properties than use a class with a primary constructor. If you need to express something complex, you will need to use more complex syntax.

  • @paululvinius990
    @paululvinius990 10 месяцев назад +16

    You missed an important fix that saves this feature a bit for me. If you instead use the same name for the readonly field and assign it with field initialization, I.e. “private readonly logger = logger ?? throw…”, they actually made it so that it is the readonly field that will be referenced throughout the class, effectively making the prim ctor param unreachable after that point.

    • @bobweiram6321
      @bobweiram6321 9 месяцев назад +1

      Too hacky. Great programming languages have a consistent syntax. No where else in C# can you declare types with the same identifier. It creates an exception to the rule. Primary constructors reduce a trivial amount of code, but increase complexity. It's a bad tradeoff.

  • @IgorRovitsky
    @IgorRovitsky 10 месяцев назад +3

    Wow, I was thinking about how I usually use "this." instead of underscore only to see couple of seconds after that this doesn't even work with primary constructors at all. As far as I can see, this feature is a huge no-go as an approach for DI and it's unbelievable that both VS and Rider recommend that refactoring

  • @pagorbunov
    @pagorbunov 10 месяцев назад +22

    In this scenario Visual Studio gives two options for refactoring: the one that would keep the fields and the one that would remove them.

    • @paulkoopmans4620
      @paulkoopmans4620 10 месяцев назад +1

      Interesting. I noticed that Nick is running in an early access program instance of Rider. I am a Rider user too and mine does not have the same behaviour. No matter what I try/do it suggests the primary constructor and always keeps the field declarations. Even for public fields.
      The other noticeable difference is that in the video he gets the constructor underlined, but for me the body of the constructor is greyed out.
      This is in .NET 7.0 / C# 12.0 / Rider 2023.2.2. I am afraid to upgrade, as what I see now is arguably better.

    • @paulkoopmans4620
      @paulkoopmans4620 10 месяцев назад +1

      Well I went ahead and installed .NET 8 as well as latest Rider versions (2023.2.3) and (2023.3 EAP 8). I cannot get it to do what Nick demonstrates here. I keep on getting the experience of it keeping my field declarations.
      It looks like this refactoring has already been improved since the recording or something.

    • @RazoRek111
      @RazoRek111 10 месяцев назад +1

      Exactly, and to me keeping the fields doesn't look bad

    • @thibaudgallanddemanneville174
      @thibaudgallanddemanneville174 10 месяцев назад

      ​@@paulkoopmans4620I need to have EnablePreviewFeature true in your cspron

  • @marianf25
    @marianf25 10 месяцев назад +15

    I actually love this new feature. It's reducing clutter in 99% of my classes. Throwing exceptions in the constructor doesn't make sense for me since the services are injected automatically, and you already get another exception if the DI couldn't inject that specific service. Regarding the readonly part, I think at this point everyone is already used with these private injected services and will not attempt to mutate them anyway. Why would anyone do it anyway? If you need more customization in the constructor, Microsoft has provided tooling to easily revert back to the traditional constructor. For me this feature is perfect as it is. Maybe the readonly modifier in the primary constructor would make it 100% perfect, but for me it already is.
    LE: I don't think this feature needs any more improvements (besides readonly). Anything else would only make it worse and more cluttery than before. I think the usecase is very clear, use it only in the classes that only inject simple DI services and nothing else.

  • @Acquire
    @Acquire 10 месяцев назад +3

    Definitely a hot take. I feel like as he's explaining why they're bad, he's also showing why they're good and the use cases. If you need actual logic in a constructor, you use a regular one. Primary constructors were created to remove boilerplate code in the common case of your constructor not doing anything of use other then fulfilling DI for example. As for readonly, that's inconsequential for most actual usage. I've never encountered a problem with someone trying to reassign a constructor parameter. If you really need the readonly fields, you can keep them, but either way, a primary constructor allows you to remove the constructor method and documentation around it because it's all superfluous to what you're trying to do. It reduces the amount of code for things don't matter. It would've been nice if they had a readonly modifier we could use on incoming parameters though. Always room for improvement, but it's unfortunate to see so many people here arguing for more boilerplate code, more waste of time and space, when talking about recent new features in the language.

    • @MikeLyncheski
      @MikeLyncheski 10 месяцев назад

      I concur. It's your class to do without as you wish. I don't see myself "accidentally" changing an injected parameter.

    • @awmy3109
      @awmy3109 10 месяцев назад +1

      Exactly, much ado about nothing.

  • @jamesbilly5324
    @jamesbilly5324 10 месяцев назад +35

    I think the primary constructor implementation has been a blunder by the design team. If you want to do anything useful (like throw exceptions as you showed in the video) then you have to write a constructor anyway. So, one of the only real use cases is dependency injection, and they made it nearly useless because the fields aren't readonly... I also think that the lack of transparency around when a primary constructor parameter actually becomes a member, as well as the difference in behaviour between primary constructors in classes/structs vs records, is a mistake.
    It's a shame because it could have been a nice feature but now it will be held back because they won't want to make breaking changes in order to fix it.

  • @vonn9737
    @vonn9737 10 месяцев назад +25

    The Kotlin implementation is much better. I hope that Stylecop will add a rule for "Don't use primary constructors" and that we are able to suppress the refactoring suggestions to use primary constructors. Feels like that as it is now, this feature just makes C# a bit worse.

    • @obinnaokafor6252
      @obinnaokafor6252 10 месяцев назад

      it is the same principle - capturing parameters

    • @vonn9737
      @vonn9737 10 месяцев назад +1

      @@obinnaokafor6252 Yes, but the Kotlin implementation allows you to set readonly with 'val' and allows you to set visibility with private etc. So is much better.

    • @igeoorge3g
      @igeoorge3g 10 месяцев назад

      Yes, kotlin with val

    • @obinnaokafor6252
      @obinnaokafor6252 10 месяцев назад

      @@vonn9737 readonly is coming in C# 13. It has been prototyped and the implementation is ongoing. So, the main principle of the parameters is to be captured, but with readonly keyword you can use them for DI

    • @alfflasymphonyx
      @alfflasymphonyx 10 месяцев назад

      Just use an .editorConfig file where you can specify to turn off the warning. You should be able to do it also by right clicking on the item with the squigly line

  • @SG_01
    @SG_01 10 месяцев назад +52

    I feel the primary constructor just makes things more complicated than it needs to be. It mixes different concerns that really shouldn't be mixed. The worst part is that it separates things that should be together (class and inheritance) by adding stuff that is potentially enormous. Not to mention what happens with class generics and their where clauses.
    In my mind you should only even consider using this feature if your class doesn't have any inheritance at all, and no generics. But even then, just use a constructor.

    • @zoiobnu
      @zoiobnu 10 месяцев назад +3

      I just can't understand how someone came up with this idea

    • @sporksto4372
      @sporksto4372 10 месяцев назад +8

      @@zoiobnu This, along with top level statements and implicit usings should be entirely removed from the language.

    • @purplepixeleater
      @purplepixeleater 10 месяцев назад

      lol wow you guys are noob xD@@sporksto4372

    • @BittermanAndy
      @BittermanAndy 10 месяцев назад

      @@sporksto4372 Agreed. All just the worst.

    • @ytxzw
      @ytxzw 10 месяцев назад

      @@sporksto4372 I agree, also primary constructors can be supressed by the .editorconfig settings, but yea, implicit usings..... meh

  • @keyser456
    @keyser456 10 месяцев назад +5

    That's okay. Just write an interceptor to stealthily change the code that's actually being executed, and all will be well. These new "features" are all amazing and _totally_ well thought out!

  • @Coen22
    @Coen22 10 месяцев назад +9

    With all the features Microsoft is adding, it would be great to have ways for turning off certain language features, like in typescript with the linter. I personally would prefer more vertical code in this case, as it is more readable, even though it is more verbose.

  • @fretje
    @fretje 10 месяцев назад +7

    The default "fixer" in visual studio doesn't remove the existing fields... That's the one you should use. It just removes the constructor body, and assigns the existing fields from the injected values. It's a small change... a little bit less code, like many changes to c# lately, which I really appreciate... makes the boilerplate go the background, and puts your business code more in front. I like it!

    • @fretje
      @fretje 10 месяцев назад +1

      Btw, there are analyzers which tell you when an "injected field" is "captured" more than once (when you for instance assign it to a field and then later use it again somewhere in a method), so it's pretty safe as well!

    • @Avangardum
      @Avangardum 10 месяцев назад +2

      This way you will have 2 available symbols: the private read-only field _service and the primary constructor parameter service, both will show up in autocomplete and it will be error prone, because someone might accidentally use the parameter.

    • @fretje
      @fretje 10 месяцев назад +2

      Like I said... there are analyzers that guard agains that (CS9124 and probably others)

    • @199772AVVI
      @199772AVVI 10 месяцев назад +3

      @@Avangardum no, if you assign the parameter to a field, intellisense only shows the field

    • @Avangardum
      @Avangardum 10 месяцев назад

      @@199772AVVI I just checked it in Rider. It doesn't show if you start typing from _, but it does if you don't.

  • @logank.70
    @logank.70 10 месяцев назад +1

    The primary constructor feature feels like a feature that should've waited for next year.

  • @davidcfrogley
    @davidcfrogley 10 месяцев назад +4

    Kotlin also allows you to pass arguments into your primary constructor and then assign them to private fields directly or within `init` blocks, so you don't _have_ to make them properties with `var` or `val` if you don't want to.

  • @Ackdari
    @Ackdari 10 месяцев назад +1

    I also don't see much value in this feature. I think this pattern mostly matches to types that just hold data (i.e. records), but I think it does not fit for types that have an private internal state.
    I also think that it make the code somewhat ugly in case you have a complex class declaration with generic parameters, interfaces and constraints on the generic types. To add the declaration on how to construct the type is imho the wrong step.
    A feature that I miss for sometime is a more elegant way to reference the type of the current type. This would allow and help to write code like `private readonly ILogger _logger;` or `public class Foo : IEquatable` more easily and would make the code more resilient to simple copy-paste scenarios.

  • @Variant55
    @Variant55 10 месяцев назад +2

    I won't be using primary constructors. The other issue is if you actually need to do something with the dependency in the constructor itself (e.g. if I have config values from IOptions I don't typically keep around the interface, instead opting to grab the values in the constructor and store those). Its just going to lead to inconsistencies in the code base, and I'm not really worried about having a few more LOC in my classes.

  • @Menicoification
    @Menicoification 10 месяцев назад +2

    You are completely right. This in not good. I would have preferred to have a keyword on my field / property etc. to signal it is part of the primary constructur. leaves the freedom of declaring the stuff as I want it, AND makes to code more horizontal

  • @js6pak
    @js6pak 10 месяцев назад +2

    the thing about kotlin is that you can still do logic in init {} so you literally don't lose anything by using a primary constructor

  • @alexanderivanov6927
    @alexanderivanov6927 10 месяцев назад +1

    If you're using the primary constructor parameters to assign private readonly fields with null checks like Nick does at 5:40, you can prevent those parameters from being used in methods by marking them with the "in" keyword. It's not designed for this use case but does the job and I dont think it will change the behavior in any way.

    • @jafar217
      @jafar217 8 месяцев назад

      I'd rather use something like required properties and make the getter part private. I think that achieves the same thing as in kotlin and is a lot more readable. Especially if there are a lot of parameters that can get very ugly when added in a constructor.

  • @ak_nex
    @ak_nex 10 месяцев назад +2

    Totally agree with you Nick! Thanks for highlighting these severe issues

  • @xaberue
    @xaberue 10 месяцев назад +1

    I couple of days ago I was asked about the usage of primary ctors, I exactly explained the same, I dislike the behaviour having non readonly fields. I also dislike to have a private readonly field pointing to the injected one since you can potentially have then code pointing to one or another indistinctly...
    I was convinced to not use it since I saw it for first time in a code snippet shared by David Fowler in Twitter, and the only gain is a few lines of code less that the IDE indeed helps you adding...

  • @jeroen7362
    @jeroen7362 10 месяцев назад +1

    I don't like primairy constructor for all the reasons you stated, and also because i think there is enough sauce already on the class definition, all interfaces, type T, : base( argument a) etc like public class MyGenericClass where T : IComparable, new() now you would also add extra params. nope, there is nothing wrong with a clear visible constructor as we have for a long time now.

  • @TheXX113
    @TheXX113 10 месяцев назад +1

    I guess Microsoft will use in future the same concept as in TypeScript:
    constructor(private readonly client: HttpClient) {}
    It is still not primary constructor, but the thing that you can declare members and parameters with one declaration.

  • @cheebadigga4092
    @cheebadigga4092 10 месяцев назад +1

    Modifiers would be a huge plus with that feature! I personally don't use it because of the flexibility "legacy" constructors give me.

  • @calvinwilson3617
    @calvinwilson3617 10 месяцев назад +2

    I 100% agree with your reasoning. I really like using the primary constructor to indicate all the different things required to create a new instance of a class, but we really need the ability to mark inputs as readonly

  • @jongeduard
    @jongeduard 10 месяцев назад +1

    Oh yes! I can confirm I have already encountered 2 clear situations where I decided NOT to accept this refactoring hint and where I felt things are really way too ambitious now.
    First of all. Where I decided that I just wanted a record instead, and where that hint was not given anymore. And although in fact that's also a primary constructor, it is in a different way.
    My class in question was representing really just value semantics without any logic, without methods. Really a data record. This should VS have recommended as the first choice.
    Second situation was where I just started writing an MVVM command definition with lambda expression in my constructor. The problem is that C# does not accept those complex code things directly on properties or fields.
    When I finished writing, the refactoring hint finally disappeared. So that was great, but made me aware of the possible mistake of refactoring too early, before having the thing thought out well enough.
    Another thing which I have also noticed is a problem with bugs. This refactoring in VS still generates broken code in different situations now. Not too great.
    Even in Mads Torgersen's demo video some bugs showed up as well I noticed.
    However, I still think that it's not bad new feature. I like it and Mads' explanation about it was really great! But things need to be thought out longer, that is clear.

  • @tekken4111
    @tekken4111 10 месяцев назад +2

    I have been thinking exactly the same about all things you mentioned. We are really missing modifiers to keep the behaviour the same.
    Another example is inheritance with primary constructors. If you would refactor something like an out of the box solution such as inheriting from DbContext, you need to pass options. But in the primary constructor, this looks like:
    "ExampleDbContext(DbcontextOptions options) : DbContext(options)"
    But now suddenly the options field is available in the full class scope! Instead of only passing the ctor value.

  • @Sander-Brilman
    @Sander-Brilman 10 месяцев назад +11

    I acutally love how primary constructors write especially for DI but the ability to add readonly to the parameters should really be a thing.
    it is also confusing that when you assign the parameter to a private field or a propperty you can still use the original parameter along side the parameter. a possible solution for this would be to add some more magic behind the sences that if you assign the parameter you can no longer use the parameter inside methods.
    overall i feel like its a great feature once its fully complete (hopefully in .net 9)

    • @aemarco
      @aemarco 10 месяцев назад +3

      Agree. The readonly and the _ really are missing here. Really wonder, that the didn´t slap a readonly on that generated field, since that would be the most obvious thing to do, given IDE even warns you to make injected stuff readonly, if never changed (and have the mutable option if you assign it to a field yourself).
      The defaults not lining up with best practices.
      Also wonder about this. not working.... even i hate it, but would be better than just omitting the _

  • @ApacheGamingUK
    @ApacheGamingUK 10 месяцев назад +2

    This has been my issue with this feature from when you first showcased it. It's not readonly, and it doesn't start with an underscore. I wish they had that as their baseline. Primary constructor arguments are converted to private readonly fields, with the underscore prefix. It would be up to the consumer to dispose instances, if required. The thing I like the least is that is messes with established coding standards, and naming conventions. You strive for discipline when coding, and then something like this pushes you into the dilemma; break your discipline with naming, or artificially suppress a compiler warning?

    • @VoroninPavel
      @VoroninPavel 10 месяцев назад

      I'd make this indeed a default arg = private readonly and underscored.
      But I'd maybe add attributes (directives?) to change this behavior per assembly/class/argument.

  • @galberts
    @galberts 10 месяцев назад +1

    I agree with you. Another downside is that you cannot easily see constructor references, now you'll get all references to the class.

  • @HamishArb
    @HamishArb 10 месяцев назад +3

    Half the refactorings change behaviour (at least subtly) 🙃
    I do think that the new way (primary constructor) looks better more often, even if you want use the backing field approach. It's clearly mainly intended for data objects, which it seems to work quite well for mostly.

  • @talwald1680
    @talwald1680 10 месяцев назад +1

    I will also add that I am trying the new c# and the main issue I ran into besides the readonly issue, and that is that I can't make the constructor internal while the type is public 😢

  • @Amit_E
    @Amit_E 10 месяцев назад +3

    The parameters in a primary ctor should have been private readonly by default, then we have the benefit of referencing them without creating a field. Now we're stuck with this implementation because changing it would be a breaking change.

    • @z0nx
      @z0nx 10 месяцев назад

      Nah, everything else is mutable by default. Doesnt make sense to make this different

    • @finickyflame
      @finickyflame 10 месяцев назад

      ​@z0nx Record creates immutable properties by default when you use the primary constructor. So no, not everything in mutable by default.

    • @z0nx
      @z0nx 10 месяцев назад

      @@finickyflame That's right. And we are not talking about records, but classes.

  • @pauldbentley
    @pauldbentley 10 месяцев назад

    The refactoring in Visual Studio maintains the readonly fields if you are using them already

  • @GandalfThePlaid
    @GandalfThePlaid 10 месяцев назад +1

    Seems like a solution in search of a problem.

  • @SuperMarcotorino
    @SuperMarcotorino 10 месяцев назад +6

    Totally agree with you, Nick. Personally I didn't like primary constructors from the start, not only for the impossibility of adding modifiers (especially the readonly one) but also for losing the private fields naming convention (leading "_"). For now, I'll just disable the warning and keep things in the old-fashioned way.

  • @joshuakolb4797
    @joshuakolb4797 10 месяцев назад +2

    It's a really good feature imo. You give up readonly, but you gain the ability to remove absolutely unnecessary boilerplate. Just about every single class I have using DI has the same boring boilerplate constructor; in the cases where something else is going on, it now gets highlighted instead of lost in the noise. This enhances readability, which is well worth the cost.

  • @MrBildo
    @MrBildo 10 месяцев назад +2

    Nick is 100% correct here. Without the readonly modifier it's not useful for 90%+ of my use cases, which is injecting services. For almost every other class I use a record. Complete waste of a feature and frankly it feels like MS is completely out of touch with how people use their framework.

    • @hermit7290
      @hermit7290 10 месяцев назад +1

      I would hard disagree. I enjoy these features that are really hacking away at the "c# is mostly boilerplate" argument. The readonly modifier IMO is the only thing that is missing but even without it I don't feel its a showstopper unless you are working in a very junior dev team. And if it is an issue then a simple code analyzer can enforce no reassigning primary constructor args.

  • @pavelzagrebelin3092
    @pavelzagrebelin3092 10 месяцев назад

    I believe there should be some kind of analyzers that check (and mark as error) assignment to primary constructor parameters.

  • @bric305
    @bric305 10 месяцев назад +9

    I use primary constructors but then assign them to a private readonly field that I use in the class. It feels like using a constructor, but with less boilerplate. The only problem is that you can access the parameters of the primary constructor anywhere (instead of using the readonly field).
    I wish it could be like angular where you can add private inside the constructor's parameters.
    Or even better, like the new angular way with the inject() method. It could result in something like this : private readonly IServuce _service = inject() or maybe with an inject keyword or decorator

    • @fusedqyou
      @fusedqyou 10 месяцев назад +1

      The `inject()` feature is a mess and it would become an even bigger mess in C#. You effectively lose the ability to pass dependencies through the constructor and are forced to use whatever dependency injection system supports it. This means I can no longer instantiate a new class without it being forced to work as a dependency. It's not an issue with Angular but it creates this unneeded learning curve.

    • @WillHausman
      @WillHausman 10 месяцев назад

      I haven't tried it yet, but one of the presentations about C#12 said that if you name your fields the same as the primary constructor parameters when setting them, then the references in the class will never use the parameters. Of course, this goes back to Nick's problem of naming convention with an underscore prefix, though.

    • @adambickford8720
      @adambickford8720 10 месяцев назад +1

      `inject()` is a giant mistake.

    • @DodgerLD
      @DodgerLD 10 месяцев назад

      @@WillHausman It does indeed work like this. I enforce the use of `this`, so it's not possible to reference the constructor arguments throughout the class, you have to use `this` to reference the fields that they are assigned to.

  • @wknight8111
    @wknight8111 10 месяцев назад

    I agree 100%. I don't like primary constructors for exactly this reason. Using immutable private fields should be the preference and the default. What primary constructors do is to add just another way that making private readonly fields is *harder*, not easier. It's a lousy feature in terms of code quality and correctness, and I don't know why it was added. records, on the other hand, are a good feature because the properties you setup in a record are readonly by default.

  • @Dimich1993
    @Dimich1993 10 месяцев назад

    You can't deny primary constructors look beautiful.

  • @eladmarg
    @eladmarg 10 месяцев назад +7

    i like the primary constructors, they're saving lines of code. only what i missing is the readonly modifier. but generally likes it better. (also didn't like the _ prefix in the first place)

  • @Ivolution091
    @Ivolution091 10 месяцев назад

    Totally agree with you. It's also not obvious for me while reading a method body if a member is a local variable or a primary constructor's parameter. And I absolutely don't like the idea of having parameters in a primary constructor which names started with '_' - too weird...

  • @Lammot
    @Lammot 10 месяцев назад +2

    Glad the community is finally coming together on the subject.
    All of the points about scoping, naming, accessibility, etc were listed in the feature discussion on github and were brushed away without much consideration. The argument was "well that's how scoping works and we already doing that on records".
    Guess now we have a dead feature that probably won't be changed in any meaningful way to preserve backward compatibility. :)

  • @lordmetzgermeister
    @lordmetzgermeister 10 месяцев назад +4

    The main use case for constructors nowadays is DI, so the primary ctor should at the very least support basic features - readonly and null check on the parameters. We could have just the readonly keyword and a hard nullability check by default, unless the dev explicitly injects a nullable type, for example.
    We don't need to support property keywords since we can assign the ctor parameters to properties and for any other logic we'd probably want the full ctor anyway.
    I think I said the same thing under your first video on primary ctors.

  • @hhcosminnet
    @hhcosminnet 10 месяцев назад +5

    We should have modifiers for making the fields private/public and read-only. We could have also a not null modifier. Perhaps it would become too much.

  • @alanbarber4543
    @alanbarber4543 10 месяцев назад +2

    Not a fan at all of this and the bigger issue of the language developers pushing to try to hide "plumbing code" like constructors, etc.

  • @krccmsitp2884
    @krccmsitp2884 10 месяцев назад

    The primary constructor feature isn't bad on its own-like with every other language feature, one has to decide whether to apply it or not. I myself haven't yet made up my head whether i like it or not. The suggested refactoring, however, is IMHO dangerous, as refactoring should never alter the behavior or meaning of the code. I'd go with disabling the related notification and refactoring for it.

  • @chris-pee
    @chris-pee 10 месяцев назад

    I was really hoping for Microsoft to add the readonly last minute, but alas. I don't even understand what use-case they had in mind when designing this first iteration.

  • @omostan
    @omostan 9 месяцев назад

    First of all, thank you for all the tutorials you provide for the developer community. It is a huge plus for all of us.
    In my opinion, I don't agree at all with your suggestion not to implement the new primary constructor because of the readonly issues. Yes, there are issues with the primary constructor, but you can still implement it without any problems if you keep the same name for the private field as in the class level or primary constructor. Using an underscore for the private field, as you have demonstrated, will certainly lead to a mutation problem if the name is different at class level. But if you remove the underscore and keep the same name, you will still have the readonly field correctly as it was before C# v12. Also this feature is still under development and needs to be implemented with care. It is absolutely not for solving problems, but rather for simplicity and costmetics purpose. I like the fact that the squiggly markers shows you where in your code it is safe to be implemented. That's just my humble opinion 😉 .

  • @NeedNoNameDK81
    @NeedNoNameDK81 10 месяцев назад

    I will just shortly state that I totally agree Nick. I was a little stunned when I first saw this feature, because I found it completely unnecessary. It is just syntactical sugar and introduces new problems as you mention.

  • @izobrr
    @izobrr 10 месяцев назад

    First, 25 years back the constructor had not be named as the class. It better be a method with a special name "this" or at least "ctor".
    Second they'd better made the primary constructor parameters-fields read-only by default and later allowed modification if people asks, but in 90% cases read-only is what is actually needed.

  • @xiety666
    @xiety666 10 месяцев назад +1

    I don't like the way how inheritance multicapture problem is handled.
    If you pass parameter to parent's constructor, then you can't use it directly inside child class.
    So what is the reason for primary constructor parameter to be visible throughout the whole child class if I can't use it?
    Here is the simplest code that produces warning:
    CS9107: Parameter 'int number' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.
    ```
    public class Parent(int number)
    {
    public void ParentMethod() => Console.WriteLine(number);
    }
    public class Child(int number) : Parent(number) //warning here
    {
    public void ChildMethod() => Console.WriteLine(number);
    }
    ```

  • @jamienordmeyer4345
    @jamienordmeyer4345 10 месяцев назад

    I like the syntax of primary constructors, but I'm with you, Nick. I'm not using them until they have AT LEAST the ability to specify readonly on the values passed in, if not also the access modifiers.

  • @gilbes1139
    @gilbes1139 10 месяцев назад +3

    100% agree with the video. Primary constructors are compelling, but the implementation is lacking for DI scenarios. Modifiers like readonly and field naming options to keep them consistent with the rest of the code base are crucial to using primary constructors in a DI scenario.

  • @nocturne6320
    @nocturne6320 10 месяцев назад +31

    The primary constructors are still a good idea, I think the only thing missing is the ability to specify the field as readonly. The Kotlin implementation is better, but considering that the primary constructor creates only fields, not properties, then why not just allow for adding "readonly" to the primary constructor? fields should be private/protected anyway, and since it is not advised to have fields as protected and instead expose them trough protected properties there is no reason for specifying the visibility in the first place. Have all fields private and only allow for explicitly specifying if they are readonly or not.

    • @bric305
      @bric305 10 месяцев назад +3

      yeah I just don't see a use case where you would want to have a public field in dotnet

    • @an_wobbly
      @an_wobbly 10 месяцев назад

      The main idea is that primary constructors would create auto-properties (or init-only properties if readonly) which is the default behavior for records, for example. One of the worst things about primary constructors is that you cannot modify the parameter in constructor chaining, but you can modify them in methods which makes no sense - literally the complete opposite of readonly lol.

    • @jamienordmeyer4345
      @jamienordmeyer4345 10 месяцев назад +3

      Especially since Microsoft also created TypeScript, which has had primary constructors for a long time (not sure if they're called that in TypeScript or not, but it's the same idea). For anyone that's not familiar with TypeScript, I can do this in a class (yes, this is in a constructor function, rather than defined on the class definition... but it's the same idea):
      export class SomeDataService {
      constructor(private readonly dbContext: DbContext){}
      ...
      }
      I don't HAVE to declare the private readonly specifiers... but if I do, they apply to the field being declared in the way that you'd think they would; dbContext is private to the class, and cannot be altered inside the class.
      In theory, Microsoft had a tougher time getting this to work with the C# compiler than they did with the TypeScript compiler, which is why it didn't ship. At least, I'm hoping this is the case, and that we'll eventually get the proper functionality. Hopefully they nail it in C# 13. I do like the syntax. :) But for the same reasons that Nick laid out here, I won't use primary constructors until I can specify AT LEAST readonly. Specifying private might not be as needed. readonly is a must.

    • @Bliss467
      @Bliss467 10 месяцев назад

      It would get remarkably verbose without var and val, tho I’d rather have that than not

    • @nocturne6320
      @nocturne6320 10 месяцев назад

      @@bric305 It's only a case with high performance structs like math types (eg. vectors, quaternions, etc.), there it's better to modify fields directly then have the overhead of a property.
      @joshuawillis7874
      I don't think primary constructors should make properties, that's what records are for. Primary constructors main usecase will be services with DI, so you don't always have to write the backing fields as well (as shown in the video),
      I think the issue with the lack of argument validation, etc. could be solved by adding in some sort of optional method, that would run immediately after the primary constructor assigned the fields their values

  • @AlFasGD
    @AlFasGD 10 месяцев назад

    Here is a very unpopular take: we are simply elongating declarations too much, and that's why all solutions look bad. Primary constructors are neat because you very often only have one constructor, and most of the logic in a constructor is assignments and an assertion.
    So what would probably be better is to have a primary constructor declaration feature that does not lie right next to the name of the type, and is instead a dedicated constructor perhaps with the `primary` keyword on top of it, which would be used to infer the declared members of the type. This would all be nicely encapsulated, and include potential logic aside from the captured members, like assertions and validation as found in many other constructors.

  • @TheOnlyDominik
    @TheOnlyDominik 10 месяцев назад +1

    I disagree, many classes, e.g. for EF, can be written more efficiently this way and like any C# feature you have to use it wisely. So use this feature, but only if it makes sense.

  • @Denominus
    @Denominus 10 месяцев назад

    100% agreed, and it's particularly bad that the recommended refactoring changes behavior. Refactorings should always preserve behavior, otherwise they become untrustworthy.
    It could work in the future, maybe, but its very half-baked at the moment.
    I feel like there are quite a few half-baked things being rushed out into C# over the last couple releases.

  • @astrowalker2629
    @astrowalker2629 10 месяцев назад

    Thank you for raising this. Unfortunately quality of design is not the best, not so long ago MS introduced "record" to realize later "record struct" would be helpful. So now as the syntax go we have "record", "record class" and "record struct". At least this one, unlike "record", is fixable (in future releases).

  • @gustavogoncalves5611
    @gustavogoncalves5611 10 месяцев назад

    For this case, when injecting services, I prefer to use records

  • @NWessel
    @NWessel 10 месяцев назад

    I do agree it would be NICE to control if it was readonly, but I only see this as a problem for juniors. The thing I love about this is, we have some developers here that loves props over fields, so our code gets very inconsistence in that sense, this fixes that issue (To some extent)

  • @gctypo2838
    @gctypo2838 10 месяцев назад

    The biggest thing that bothers me about C#12's primary constructors is that they don't default to readonly like they do in records. They should absolutely be only for readonly fields. I don't know why they decided to make inconsistent with the existing usage of this syntax. I never would have known this syntax and code action actually makes it mutable if it weren't for this vid. Great way of tricking devs into screwing up their code, nice going MS and JB. Not happy with 90% of C#12's additions so far.
    Maybe it could be the FP talking but in general I've gradually found myself more and more irked by C#'s "mutable by default" pattern in its design. I've seen some truly unholy things when a dev just slaps everything into public mutable fields and changes them willy-nilly. Immutability makes a world of difference in keeping things clean and comprehensible.

  • @MarshallRosenstein
    @MarshallRosenstein 10 месяцев назад

    I find the feature most useful when defining classes that derive from a base class but differ primarily in the constructor parameters.

  • @garrypshonik
    @garrypshonik 10 месяцев назад

    Thanks for making this video. I have been raising the same concerns. VS 2022 suggests two refactorings - "Use Primary Constructor" and "Use Primary Constructor (and remove fields)". The first option still does the right thing and assigns the primary ctor parameters to the readonly private fields. The second option removes the fields like you show in your video. I haven't been able to find a way to disable the second refactoring VS 2022, but I'm pretty sure you can in Rider, which makes the Rider suggested refactoring keep the readonly private field assignments.

  • @haxi52
    @haxi52 10 месяцев назад

    I hate underscores, so thats a plus in my book. I agree with the readonly part. I think I'll try it for a while.
    I'd rather they focus on making unit tests easier; interfaces are not supposed to be used like this.

  • @cn-ml
    @cn-ml 9 месяцев назад

    I just used primary constructors for my complex projects because they reduced the amount of boilerplate code massively. Previously i used the AutoCtor package to generate constructors automatically with the private readonly fields which is more or less the same, except for the readonly aspect. I think its still a good feature, but i think the C# team really should have decided to make this readonly by default.

  • @kinsondigital
    @kinsondigital 10 месяцев назад

    I 100% agree with this. Velaptor for one has a lot of injected services and types and I will not be converting those to primary constructors for this exact reason. I also hope that they have plans to be able to improve this. I am hoping that they don't put themselves on a slippery slope of releasing features just for the sake of releasing features.

  • @jonatansoderberg
    @jonatansoderberg 10 месяцев назад

    I really like injecting via primary constructors. But I agree with the problem of them not becoming readonly. Perhaps a solution by setting an attribute on the class and creating a source generator to generate the private readonly fields is possible

  • @pauldbentley
    @pauldbentley 10 месяцев назад

    I almost think the default for primary constructors should be readonly fields with underscore. As that is how we have all been trained to write our code
    so class Foo(ISender _sender) gives you a readonly ISender _sender - but the actual lowered code would generate a constructor without the underscore so Foo(ISender sender)

  • @rwj_dk
    @rwj_dk 10 месяцев назад +2

    I'm trying out Primary Constructors at the moment, and are generally happy with the reduction of overhead ... Getting used to not having _fieldname is a bit hard... Also considering if one should start doing private properties to replace private readonly fields (Yet to see the drawback, but there might be some...)

  • @UDarkness
    @UDarkness 10 месяцев назад

    I think they have very different semantics to fields. Also they are not equivalent to kotlin, only in syntax. The parameters are meant to be captured by either a field, methods or property, the visibility modifiers can be expressed there. A very nice feature of the parameters is that only one member takes ownership of them and other members cannot access them any longer. The params behave more like closures in lamdas (magic fields under the hood).
    I didnt try them yet on prod, but I am interested 😊

  • @Matlauk
    @Matlauk 10 месяцев назад

    Because of the complete lack of control over the generated fields, I was not able to use primary constructors. There is really only one specific use case for them at the moment and because I want my code style to be similar throughout the application, I can't use them.

  • @F1nalspace
    @F1nalspace 10 месяцев назад

    Yeah i fully agree, until we dont have a way to validate primary constructor arguments and support private readonly it makes no sense to use it at all. For me this looks just like a way to write less code, relying purely on the input to be always valid.
    I will definitily not use primary constructors them until microsoft solves those problems. I even dont use records, because i always end up requiring special logic for Equals/Hashcode and ctor anyway, so either a struct or a class.

  • @kARMIN-gt4qh
    @kARMIN-gt4qh 10 месяцев назад

    it possible to use it like this:
    public class Foo (int id)
    {
    private readonly int _id = id;
    }
    Although it is possible to change the value of the id within the class, the value of the _id field remains unchanged.

  • @Looooooka
    @Looooooka 10 месяцев назад

    I'd scrap the whole idea. Contructors aren't where we're "losing" time and productivity.

  • @djoobstil
    @djoobstil 10 месяцев назад

    You make a great point and I have the same problem with this feature.

  • @neverovnikita
    @neverovnikita 10 месяцев назад +3

    I don't understand what the problem is, just don't assign anything to the parameters passed through any constructors. After it stopped being readonly, I have no great desire to assign anything there

  • @VladyslavHorbachov
    @VladyslavHorbachov 10 месяцев назад

    I'd like to see init attribute in the primary constructors instead of readonly.
    Just like we have init only setters.

  • @Raziaar
    @Raziaar 10 месяцев назад

    Yeah I had this issue too. Had to add it to my global suppressions.

  • @CRBarchager
    @CRBarchager 10 месяцев назад +2

    I'm happy that you feel this way. I never felt right about this feature, compared to Records. Following the naming stardards a field should be private with _camalCasing and a property should be public with PascalCasing. Now we are mixing them together and now suddenly a parameter is public with camalCasing or _camalCasing. - Not taking anything else into account this made me not liking this. Not being able to see the field is global to the class is abother thing. Without the underscore I would assume it as being a private variable to the current method but now it's global. This doesn't makes sence to me.

  • @LasseVågsætherKarlsen
    @LasseVågsætherKarlsen 10 месяцев назад +1

    I still like primary constructors, but I think that Rider is suggesting it a bit too aggressively. I have plenty of classes that should not be records that can benefit from the different syntax, but I agree that Rider is suggesting to use it way too much at the moment.
    Hopefully there is/will be options in the settings to control this, and a better default.

  • @adrianbrink4875
    @adrianbrink4875 10 месяцев назад

    Great video :) I think you should put the feature name in the description to make this video easier to find.

    • @gbjbaanb
      @gbjbaanb 10 месяцев назад

      it wouldn't be clickbait if he told you what it was about in the video title :)

  • @kurokyuu
    @kurokyuu 10 месяцев назад +1

    I suppressed the recommondation, since this isn't really working good currently. All the points you mentioned are also my problems I have with this feature, so, currently I woulnd't use it and have no idea where this could be a good idea to use~

  • @franciscovilches6839
    @franciscovilches6839 10 месяцев назад

    Thank you for addressing this!! I like c#12 but this has been a splinter

  • @fibs9456
    @fibs9456 10 месяцев назад

    During refactoring (yes i planned to adapt my whole codebase ), i struggled with exact these points you mentioned and after 2 classes i reverted all refactored classes 😅

  • @fatihcihanhizlikan1427
    @fatihcihanhizlikan1427 10 месяцев назад

    I didn't agree with you about minimal APIs. I agree with you on this one completely.

  • @peteruelimaa4973
    @peteruelimaa4973 10 месяцев назад

    Bought your integration testing course. Very good! Can only recommend ❤

  • @ilh86
    @ilh86 10 месяцев назад

    Something else to add to the pile of new features added in the last couple of versions for the sake of "simplicity" that end up making the code harder to reason about. I'll not be touching this along with implicit usings and top level statements.

  • @rasmusdybkjr5053
    @rasmusdybkjr5053 10 месяцев назад

    I like this feature because it removes boilerplate in a lot of simple classes, and this imo makes them more readable.
    Most of my constructors do nothing else than assigning arguments to private fields. And if they do, then I can stick with a good old constructor, what's the big deal?
    Readonly arguments would be nice - but really, who's dumb enough to modify the arguments anyway? And if someone is dumb enough, they would also be dumb enough to remove the readonly modifier on a private field. So again - what's the big deal 😁

  • @ApacheGamingUK
    @ApacheGamingUK 10 месяцев назад

    And how are you supposed to add XML documentation to a primary constructor, if it's also the XML documentation for the class itself?

  • @ryankueter9488
    @ryankueter9488 10 месяцев назад

    Isn't that what records are for, to create immutable classes or structs? When those were released, everyone said that they would start using those. And then they were forgotten about :/ The functional programming language features appear to be struggling in C#.

  • @kajbonfils
    @kajbonfils 10 месяцев назад

    I really like the syntax. But simply doesn’t understand why it’s not read only by default

  • @amitkumdixit
    @amitkumdixit 10 месяцев назад +2

    I will keep primary constructors. It makes code more readable and expressive

  • @allinvanguard
    @allinvanguard 10 месяцев назад +2

    Generally I really like primary constructors, but also ran into occasions where dependency injection wouldn't work for Filter Attributes and the likes.

  • @StarfoxHUN
    @StarfoxHUN 10 месяцев назад

    Well it feels like a bug to me tbh, or just really that they added readonly to the primary constructor, integrated it into Intellisense and documentations, but last moment they had to remove it for some reason but forgot about removing it from other places.

  • @GrimReaper160490
    @GrimReaper160490 10 месяцев назад

    Yes i was just looking at this the other night, thinking hang on this is a big issue in this specific use case! i hope they allow for a similar experience to kotlin allowing scope to be set in a minimal way.

  • @zx2167
    @zx2167 10 месяцев назад

    The question that I have is why are the fields in the primary constructor not private and readonly by default (or just completely)? The way I would expect to use them is either as a readonly private field or an initializer to something else. I'm not sure that allowing the fields to be modified is really the correct design choice since these fields are intended to replace the parameters to a constructor (which "disappear" so to speak once the constructor has finished) rather than replacing the fields of the class.

  • @Philippe42460
    @Philippe42460 10 месяцев назад

    100% agree with you @nickchapsas.
    This is quite surprising that this feature was left as is because in typescript for example (a language also created by Anders Heljsberg) the primary constructor can get more details for the its parameters like "public" or "private" and also "readonly". So why not in c# this time?