Reviewing your "Best Practices" in C#

Поделиться
HTML-код
  • Опубликовано: 3 июл 2024
  • Use code TRANSIT20 and get 20% off the brand new "From Zero to Hero: Messaging in .NET with MassTransit" course on Dometrain: dometrain.com/course/from-zer...
    Become a Patreon and get special perks: / nickchapsas
    Hello, everybody. I'm Nick, and in this video, I will review your best practice recommendations for C# and .NET. There are many guideliness, best practices and preferences and in this video I will give you my take.
    Workshops: bit.ly/nickworkshops
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: github.com/Elfocrash
    Follow me on Twitter: / nickchapsas
    Connect on LinkedIn: / nick-chapsas
    Keep coding merch: keepcoding.shop
    #csharp #dotnet

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

  • @ElvinGonzalez
    @ElvinGonzalez 16 дней назад +125

    Never, ever, return a null collection from your methods, if you want to represent no results, return an empty collection instead. Returning null collections forces you to do null checks before every loop and probably nest your code inside an if, that in turn makes the code harder to read. This is specially important if someone else is mantening your code, they won't know you are returning null collections until the NullReferenceException blows in their faces. Thanks to the nullable reference types in C#8 this issue can be more evident due to the compiler warnings, but it can still be turned off.

    • @AkosLukacs42
      @AkosLukacs42 15 дней назад +12

      And now you can return an empty collection just by "return [];" the compiler will handle the rest. When I checked it did compile down to Array.Empty and similar code, so it won't allocate.

    • @MateuszOzimekTheGreat
      @MateuszOzimekTheGreat 15 дней назад +2

      Returning nulls is not nice but for initializing list properties there are situations where it makes sense to distinguish null and empty lists. Let's say I want to make a patch like operation on my model, a null value would mean I don't want to change the list, an empty list would mean I want to clear the list. I've found this more common in immutable models:
      article = article.With(title: "Test", tags: new[] { "test" })

    • @diadetediotedio6918
      @diadetediotedio6918 15 дней назад +2

      @@MateuszOzimekTheGreat
      In this case I would say the problem is more on the lack of expressivity C# has without things like discriminated unions, but if you are using nullable references (preferentially as errors) it should not be a big problem to do what you said.
      I would do it like this:
      record SetList(params string[] Items) : IChange;
      record ClearList : IChange;
      record DoNothing : IChange;
      then
      article = article.With(title: "Test", tags: new SetList("test"))
      and in new C# versions with records you could do even better with the native syntax like just specifying what you want, this way:
      article = article with { Title = "Test" };

    • @djp_video
      @djp_video 15 дней назад

      So if you, for example, go to retrieve a customer record (customer ID 12345) and it doesn't exist, you'd rather return a dummy record than a null? That doesn't make any sense.

    • @noctemcat_
      @noctemcat_ 15 дней назад +1

      ​@@djp_videoin this case it would be a collection of customer records, and you would return an empty collection

  • @ghaf222
    @ghaf222 15 дней назад +33

    My 4 top tips:
    1. Always be concise. This is very important as it can make your code easier to read for others which is very important and more readable, which everybody likes.
    B. Consistency.
    4. Attention to detail.

    • @Damien-km1vl
      @Damien-km1vl 14 дней назад +2

      Concise, but not too much. Sometimes it's easier to understand code that takes a few more caracters/lines

    • @SkyyySi
      @SkyyySi 13 дней назад +3

      ​@@Damien-km1vl Their whole comment is a joke btw

  • @TysonGibby
    @TysonGibby 15 дней назад +13

    NULLABLE REFERENCE TYPE video pretty please! Would really love to see a best practice video on handling programming with nullable check turned on.

  • @Arcadenut1
    @Arcadenut1 15 дней назад +10

    The nice thing about standards is there are plenty to choose from.

    • @alizia7114
      @alizia7114 14 дней назад

      Funny enough majority of them don’t conflict

  • @billy65bob
    @billy65bob 15 дней назад +4

    A tip from me: ALWAYS clearly signal your intent in BOTH your function names AND your return types.
    The usage criteria and the side effects should be intuitive, and the design itself should discourage shotgun-to-foot scenarios.
    E.g. if modifying a collection will not work as expected, only return a IReadOnlyCollection to pre-empt such bugs.
    If your method is expected to silently fail, give it a Try_ prefix or an _OrDefault suffix.
    It is unavoidable that people will have to guess what your code/functions do, but you should do what you can to ensure that their first guess is correct

  • @antonmartyniuk
    @antonmartyniuk 16 дней назад +44

    Considering best practices what I find useful:
    1. turn on nullable reference types.
    2. treat warnings as errors
    3. set maximum level of analyzer
    4. treat analyzer warnings as errors
    5. use editor config
    6. use Directory.Build.Props with all the previously mentioned options + add Mezianto and Sonar Analyzers

    • @TraderRoWi
      @TraderRoWi 16 дней назад +3

      Those analysers are a good suggestion.

    • @TraderRoWi
      @TraderRoWi 16 дней назад +3

      Just tried them. Both have documentation and examples when you click on the warning. Nice!

    • @fusedqyou
      @fusedqyou 16 дней назад +2

      100000% this. I do this with every single project nowadays and it helps a ton in writing readable and reliable code.

    • @JacobNax
      @JacobNax 16 дней назад +1

      I would only disagree with nullables. Personally i prefer to not use any nullable types anywhere in my project.

    • @logantcooper6
      @logantcooper6 15 дней назад +1

      ​@JacobNax that's the most important one!

  • @Zullfix
    @Zullfix 10 дней назад +1

    This video is hilariously convenient timing. I literally just had someone show up in my Github repo with a 50k line change PR where they reformatted the entire project with their personal ReSharper settings and labeled it "best practices."

  • @dhollm
    @dhollm 14 дней назад +3

    When you switch on an enum, throw an exception in the default case, so when someone adds to teh enum later you can't overlook that spot.

  • @TheOnlyDominik
    @TheOnlyDominik 16 дней назад +5

    - KISS (Keep it simple and stupid).
    - Use less Interfaces.
    - Use less Inheritance.
    - Use dynamic type, if you need it! (e.g. COM interop)
    - Do not use every abbreviation that makes the code unreadable!

    • @claylenhart
      @claylenhart 13 дней назад

      Yeah was going to say “dynamic” was made for COM, and COM is long gone now (for 99% of C# developers)

    • @TheOnlyDominik
      @TheOnlyDominik 13 дней назад

      ​@@claylenhart This has no influence on my answer, there are certainly other possible applications.
      Tip: never generalize, that always excludes. This is especially true in politics.

  • @diadetediotedio6918
    @diadetediotedio6918 16 дней назад +18

    2:50
    This is all people that defend the "don't use var" do, false examples and false equivalencies. If you name your variable myVar and your function GetLast (without context) then it is obvious that it will be a problem, heck, it will be one even if you use the type instead of var, GetLast() is still ambiguous without context because of the bad naming.

    • @smathlax
      @smathlax 16 дней назад

      Sometimes even good naming won't fix this problem though, and explicitly declaring the type helps.
      For example, suppose we are dealing with an IEnumerable. Then what should we rename the GetLast() method to? GetLastCloseSquareBraceSymbolToken() seems a bit overkill, and if anything is difficult to read. A nice middle ground might be GetLastToken(). But then you don't know what specific type of token it is.
      CloseSquareBraceSymbolToken lastToken = GetLastToken();
      The above seems like a perfectly decent solution to me, and is more readable than any alternatives in my opinion. The other alternatives that I can think of are:
      var lastToken = GetLastToken(); // I don't know what type this is now
      var lastCloseSquareBraceSymbolToken = GetLastToken(); // hard to read and I'm still not 100% sure of the type because the name could be wrong
      var lastToken = GetLastCloseSquareBraceSymbolToken(); // as above
      var lastCloseSquareBraceSymbolToken = GetLastCloseSquareBraceSymbolToken(); // ridiculously hard to read
      I think that my opinion here is similar to the "don't use comments" mindset. Comments can be wrong and thus can't be trusted, however code can always be trusted. Likewise, variable names can be wrong due to mistakes or poor conventions, but an explicitly declared type cannot be wrong.

  • @SuperLabeled
    @SuperLabeled 16 дней назад +5

    Be flexible, not everything is black and white. The use of vars is a great example. 20% of the time you don't use them, and that makes sense. I worry tha leads put so much weight on consistency just so that everything is the same no matter what.

  • @diadetediotedio6918
    @diadetediotedio6918 16 дней назад +27

    8:39
    A defect I see in C# is that they still treat packages compiled without nullable reference types enabled as non nullable by default, where they should treat all return types of everything as nullable by default. This gives a false sensation of security, much like some passes between kotlin and java in the JVM world.

    • @rowbart3095
      @rowbart3095 14 дней назад

      reference types are already nullable. the nullable option sucks

    • @diadetediotedio6918
      @diadetediotedio6918 14 дней назад +1

      @@rowbart3095
      I think you don't understand what I'm saying, but sure bro, whatever you say.

  • @Matt23488
    @Matt23488 15 дней назад +2

    I always use var everywhere in C# and have for years now. I like how it horizontally aligns all your variable declarations. I almost never need to know the type of the thing I'm looking at because it's obvious from the context. It seems the only time I'm ever needing to look at the type is if it's code I didn't write and I need to fix something in it. And if it turns out it's a custom class, I'll probably need to just go to definition anyway so there is literally zero downside in my experience.

  • @tanglesites
    @tanglesites 15 дней назад +2

    In general High Cohesion, Low Coupling. Readability means keeping things together that belong together. Be consistent. Types are not only for the compiler, anyone that has written JS knows this. Be minimalisticly explicit. Interfaces over abstractions and misdirection.

    • @lordmetzgermeister
      @lordmetzgermeister 15 дней назад +1

      those tips are so general that they're almost generic :D

  • @Birb-
    @Birb- 16 дней назад +6

    I make Rider use "use var when evident", only when it is clear to the reader that this returns something of that type, otherwise the type

    • @dand4485
      @dand4485 15 дней назад +3

      Define 'evident'... As long as i've been doing this, consistency is more important, either always use var or not at all... It is surprising to me at times, might say we all have our own backgrounds, and i've worked with some really smart people, sometimes that background people have be it programming backgrounds some things that may be evident to one person, because of "history" or convention... Something obvious to one from a person's experience may or may not make something ah it should be ...

    • @celinedrules
      @celinedrules 15 дней назад

      ​@@dand4485var playerSprite = new Sprite();
      Pretty evident playerSprite is of type Sprite.

    • @andrry_armor
      @andrry_armor 15 дней назад

      ​@@dand4485Evident - is when you see the type twice: in new declarations or in explicit cast (sometimes also for conversion methods like "ToString()"). The other forced use case is anonymous class instance.

  • @Bliss467
    @Bliss467 15 дней назад +1

    Two things make it evidently clear that coders hold onto explicit types because of tradition:
    * modern languages have implicit types baked-in, and idiomatic to leverage, e.g. Rust, Kotlin, … (also, resharper suggests always using var)
    * the same explicit-type-proponents never notice the implicit typing inherit to newer language features and conventions, e.g. lambdas, chained methods, …

  • @simongeering
    @simongeering 15 дней назад +3

    The one usecase I have found in the real world for dynamic is when you have to get deep into COM interop and old school ole office automation. Thankfully now a day's a lot of that can be done via graph or XML based APIs.

    • @gilroitto
      @gilroitto 10 дней назад

      Whenever I get a dynamic returned to me I panic and build a wrapper to be able to thoroughly test and contain processing of this demon.

  • @ruslan_yefimov
    @ruslan_yefimov 15 дней назад +2

    Best advice is following Rider tips and your common sense first... It covers almost all the bad things you could do

  • @AkosLukacs42
    @AkosLukacs42 15 дней назад +4

    The "== null" overload comes up again and again, but who has seen an equals overload so badly written that breaks null check?
    Or is that just the new "linq is slow"?

    • @DaremKurosaki
      @DaremKurosaki 14 дней назад

      As mentioned, it's mostly because of Unity. Its overload makes it so that is null and == null are never reliably equivalent.

  • @shauncurtis4466
    @shauncurtis4466 14 дней назад

    A few thoughts:
    1. Enable nullability - Agree entirely.
    2. Pass requests, return results between layers not raw nullable objects.
    3. CQS - goes with above.
    4. Never request/return unconstrained lists.
    5. Be Consistent.

  • @lordmetzgermeister
    @lordmetzgermeister 15 дней назад

    Few things to add:
    Use vertical slices in multi-domain apps. It helps so much with navigating through the codebase.
    Make interfaces only where it makes sense - multiple implementations and for testing (as in "this class will have to be mocked in my tests")

  • @antonmartyniuk
    @antonmartyniuk 16 дней назад +5

    Null can blow in your face. Dancing now 😅

  • @ouroborusseven
    @ouroborusseven 15 дней назад

    When transitioning to C# from Javascript: Linq is the equivalent for `map`, etc. (all under IEnumerable methods and extensions); `var` everything (just don't try to switch types); `Console.WriteLine` is your new `console.log` (though not as friendly); tuples are your new custom object (also not quite as friendly). (I'm only half-kidding.)

  • @bithon5242
    @bithon5242 13 дней назад

    Nick is definitely my favorite .NET Core C# 8 Greek Microsoft MVP developer with blue eyes and more than 100k subscribers on RUclips rn

  • @CharlesBurnsPrime
    @CharlesBurnsPrime 12 дней назад

    I have absolutely used the dynamic keyword in clean code. For example, sometimes you need to query a database but cannot know the types or columns ahead of time. A generic database to web service, for example. Query with Dapper to a dynamic, and immediately return as serialized json.
    I haven't really found any other good reason to use it, but dynamic is far nicer than using the wrote ADO driver.
    Just because you have never found any for dynamic does not mean that one does not exist.

  • @bobamos8508
    @bobamos8508 16 дней назад +9

    Dynamic from dapper can be quite usefull you can handle the sql direcly and return the result for the api without having to maintain an object

    • @BeatsByYari
      @BeatsByYari 15 дней назад +2

      ValueTuples are a better choice in that case, almost 0 extra work but you get auto-complete and no unnecessary possibilities for runtime errors. If you do need a lot of members, just create a class, it’s not that hard

  • @lovyNOM
    @lovyNOM 14 дней назад

    One convention that I always prefer is specifying ordinal or string culture when doing string comparison or parsing (unless it's a user typed string). Such as myString.Contains("-hello", StringComparison.Ordinal) or float.TryParse(myString, NumberStyles.Float, CultureInfo.InvariantCulture, out float myFloat).
    I do this because once I encountered a bug that was not cough in testing but in production some users where experiencing weird behaviours that we struggled to replicate. This was because some XML files where being parsed using device culture resulting in numbers like "0.5" being parsed as the number 5 depending on the users device culture. Unless it's for user input, it's better for QA and reducing edge cases to have the code work the same on all devices wherever possible.

  • @facephonesy
    @facephonesy 10 дней назад

    Depends on the project, if you are working with a team in a company then you kinda have to go with the company's best practices. If you are working alone though, I see many developers that waste a lot of time on refactoring, and trying to write perfect code. In my opinion, your cose just have to be designed to adapt to future update easily. And it has to work. Other than that if you have the disease of perfecting code, you will spend 3 times the development time and no body will know nor care about how many times you reactored your 100 classes of single responsibility to perfection to handle a couple of api requests.

  • @dvanrooyen1434
    @dvanrooyen1434 13 дней назад

    The hardest thing to do is do it simply. It takes a lot of experience and diligence to take a complex domain and build out simple systems - when devs dont fully understand a domain the system is built like spaghetti.

  • @alfonsdeda8912
    @alfonsdeda8912 15 дней назад

    Hi Nick, very great video!
    I have used the Dynamic keyword One time when i hard a generic interface and i used It in a not generic form, because i couldn't get the type at runtime, is this a possible use case or would you have any different solution than ti make the form generic obviously.
    Thanks in Advance.

  • @ChristianHowell
    @ChristianHowell 3 дня назад

    The best practice I've found is to get the best DBA handling the data... And take care with the data... It's all classes whether it's ECMA, Java, Python or C#... LET is the preferred though because of the temporal dead zone and scope in ECMA...
    Also try to keep your const values in a resx or struct or class file... Code looks better with no strings or numbers... That's what Fluent tries to solve...

  • @paulkoopmans4620
    @paulkoopmans4620 15 дней назад +1

    @1:30 Well those so called "weird" variable names stem from the fact that an IDE, if you can call it that, was nothing more than a text editor. Syntax highlighting was making keywords bold, you had 80 columns and 30 lines on your single colour monitor. You had to switch from editor back to the command line to run a compiler and then a linker. In order to get some understanding you sometimes had to print some code out to your chain feed matrix printer. If you go even a bit further back in time; 1980-1990 C64 BASIC for example would have 2 letter limit for variable names, 40 columns 25 rows. and everything had to include line numbers and navigation was goto and gosub.

    • @andrry_armor
      @andrry_armor 15 дней назад

      But nowadays devs do code reviews and often it is not in the IDE, so the rules are still useful.

  • @lordmetzgermeister
    @lordmetzgermeister 15 дней назад

    var: Yes, always.
    I explained in a comment on one of your previous videos that this comes with an entire style of coding. As both you and the reddit comment said, with var you have to name the variables in a way where you don't need the types.

  • @lordmetzgermeister
    @lordmetzgermeister 15 дней назад

    One class per file: It depends.
    A service's interface would be in the same file. Multiple records per file, yes. I usually also place classes of a multi-class pattern in the same file - strategy, state etc.

  • @lordmetzgermeister
    @lordmetzgermeister 15 дней назад

    LINQ: Yes.
    When performance and deadline are not an issue I'd even write complex LINQ queries just to challenge myself to create functional piece of code rather than declarative, which is more natural to me. Those are still readable in a way that you don't have to care what happens inside - it's just like imperative programming.

  • @torrvic1156
    @torrvic1156 11 дней назад

    Nick is a very smart boy! It is incredibly how smart can be children nowadays. I wonder if he even finished a school. He is a genius child!

  • @ApacheGamingUK
    @ApacheGamingUK 15 дней назад

    With `dynamic`, it is used heavily within OrchardCore. What would be the best practice to cater for these scenarios? When you have to use dynamic, because you are fed it from the CMS, what is the best practice for mapping it to a static type?

    • @tymurgubayev4840
      @tymurgubayev4840 15 дней назад

      Not quite sure if applicable, but take a look at source generators

  • @ChristianHowell
    @ChristianHowell 3 дня назад

    VAR has changed drastically in C#... With default constructors, I use var = new ()" and in the opposite for Framework classes I use variable = new (); and pass in any params...

  • @marthinch
    @marthinch 16 дней назад

    Some basic rule from me is handling nullable values as much as I can and also include the exceptions.

  • @Velociapcior
    @Velociapcior 16 дней назад +4

    Hey Nick, can you create a video about using directives inside namespaces (we're using .NET Framework, so no File scoped namespace) and why it's not good? I will show it to my TechLead :D

    • @caunt.official
      @caunt.official 15 дней назад +1

      No way. There’s still someone who code for Framework?

    • @ThekillingGoku
      @ThekillingGoku 15 дней назад +3

      @@caunt.official Of course there is. Many, MANY people. Why wouldn't there be?

    • @yegorandrosov6334
      @yegorandrosov6334 15 дней назад +2

      you can use file scoped namespace if the .NET Framework version is not really old, you can set the language version to latest. I am on 4.7.2 and use all language features

  • @nickst0ne
    @nickst0ne 15 дней назад

    C# non-ambiguous, like all languages: I'm mostly a backend developer but when I've dug my hands into some frontend code, I've seen lots of JavaScript that's impossible to understand because people chain many methods, and calls to properties, and higher-order functions. And no, I'm not talking about some fluent syntax.
    I've also seen some 500-characters lines of C# but that's rare and it was mostly some Linq query that could easily be rewritten vertically.

  • @MarkGjoel
    @MarkGjoel 15 дней назад

    So of course, turn on nullable types. When you have done that, USE THEM! Stop returning random values like -1 or string.Empty when you don't have a result. You can return null and the consumer of your method will be forced to do a null check. On the contrary, if you return an object, the behaviour is just as obfuscated as if you don't use nullable types (There are of course exceptions where an object may work as a perfectly fine non-work item).

  • @darthruneis
    @darthruneis 15 дней назад +1

    I dislike having multiple different classes (or class + its interface) in the same file, with the exception of generic classes + a default non-generic version of it (I don't like having like ClassNameT.cs for the generic version of the class). The reason for my dislike is just for findability - I really dislike not being able to just do a file search in a solution for the class I'm interested in. I prefer to just go to definition/implementation when possible, but that's not always readily available depending on the situation. A simple file search is a quick and effective way to find what you need, but if you have multiple classes in a file, that goes out the window.

    • @Liphi
      @Liphi 14 дней назад

      You can use member search, "go to definition" or just global text search. I have my file explorer closed almost all times so multiple classes/records/interfaces in one file is not problem for me

    • @darthruneis
      @darthruneis 14 дней назад

      @@Liphi You mentioned file explorer, but to be clear, I was referring to solution explorer. Large solutions/repos (for example, my work one is 20+ years old) is really slow to do a repo-wide full text search. Its something I have to do regularly for sure, but if I just want to get to a class quickly, its not the fastest way.

  • @M3lodicDeathmetal
    @M3lodicDeathmetal 12 дней назад

    I always use var except when using the base class or interface of an object, i.e. I prefer `BaseClas obj = new ChildClass();` over `var obj = (BaseClass) new ChildClass();` because I think it makes the intentions clearer. Otherwise, `var` all the way.

  • @MadafakinRio
    @MadafakinRio 7 дней назад

    I don't use nullable because I don't wanna be bothered to deal with it every time. When I get a Null ref exception then I'll deal with it.

  • @awright18
    @awright18 16 дней назад +2

    There are some very specific cases where dynamic is the only option, but in most cases its not necessary.

    • @dand4485
      @dand4485 15 дней назад +1

      Agreed, the main reason i like C# over javash1t, c# is strongly-typed and hate remembering the hours debugging something because javashit's oddities and strings are not check-able at compile time 🤡. I would even love to see a warning "invalid use of var -- a strongly typed object required..." and set the flag "Warnings as errors". Heck i would love to see both 'var' and dynamic elevated to require "unsafe code" 😳 Looking back there are very, very few times where dynamic was ever needed, but have to admit it was it really was the best/only way.

    • @lepingouindefeu
      @lepingouindefeu 15 дней назад

      ​@@dand4485javashit lmao

    • @fenderjazzbrian
      @fenderjazzbrian 15 дней назад

      @@dand4485maybe write a Roslyn analyzer for this?

    • @diadetediotedio6918
      @diadetediotedio6918 15 дней назад +3

      @@dand4485
      'var' is not weakly typed in C#, you are confusing things. In C# 'var' is just for type inference, it is still strongly typed.

    • @dand4485
      @dand4485 15 дней назад

      @@diadetediotedio6918 Okay fair enough
      var x = object.foo();
      What type is x?

  • @AndersReinhardtHansen
    @AndersReinhardtHansen 15 дней назад

    This is not necessarily specific to C# but; Use editorconfig or other linting software whenever you as a team settle on a convention, that is typing over var or brackets or whatever.
    The reason for this is then you never have to discuss it on a code review again. Thus you use less time on this.

  • @dkabracadabra
    @dkabracadabra 15 дней назад

    kinda silly about naming... naming things is one of the major problems in coding. i would say:
    - use ide, extensions which analyze your code while you are learning. i.e. jetbrains products
    - learn yield and return ienumerable instead of collections. you can alwas ToAnything outside. but be aware of multiple enumerations then
    - ask youself when it is better to chain queryables and when to serialize
    - dispose disposables. but there are known exceptions from this rule
    - you can easily shoot in your leg with generics nesting. avoid. especially in DI
    - overloading with new keyword is a last resort. same as dynamics
    - learn about async and it's gotchas. it's not the same as in js
    - converting Int64 as number into json can lead it to be another number in js code
    never really liked nullable checks enabled... i place check when i think it could be null. otherwise code will throw and i am ok with it because of proper exception handling. i am to lazy to pinpoint in every part of code about nullability of nullable types. and? yeah, nuget packages problem.

  • @ryanelfman3520
    @ryanelfman3520 16 дней назад +2

    For me:
    Limit the use of inheritance if possible. This warrants its own big discussion.
    Always pass cancellation tokens and don’t make them have default method parameters.
    Build whatever it is as fast and as dirty as possible to get the learning experience then clean it up afterwards. As opposed to getting into analysis paralysis. (Tracer bullet pattern)
    Use IAsyncEnumerable when available and possible for I/O ops
    Internal sealed classes by default unless they really need to be public and inherited.
    No magic strings and numbers
    I’m sure there’s a lot more I can’t think of right now

  • @nanny07
    @nanny07 16 дней назад +1

    What do you think about tuples? I have seen many controversial opinions about them

    • @igiona
      @igiona 16 дней назад +2

      Named Tuple, which are out there for a while now, are perfectly fine when the object is kind of local and doesn't have to be passed around to too many functions..
      Anonymous tuples though should be avoided imho.

    • @keyser456
      @keyser456 15 дней назад +1

      They're so convenient in a pinch, but also can get out of control quick. As mentioned by someone else, in a very local use they are harmless enough. Creating designated classes/structs/records for very niche combinations of return data isn't just tedious, it adds clutter both to the codebase and intellisense/autocomplete.

    • @diadetediotedio6918
      @diadetediotedio6918 15 дней назад

      I don't know if people should listen to "controversial opinions" on basic mathematical concepts.

  • @gilroitto
    @gilroitto 10 дней назад

    Personal opinion but operator overload is evil, it forces you to know everything about the library or codebase instead of trusting what you see. It’s a throwback to old C++ days where you had to be an expert on the specific solution to be able to be productive and do safe changes.

  • @Alguem387
    @Alguem387 15 дней назад +1

    its 2024 god dam it if you have any doubt about the type just mouse over it or jump to definition no reason to clutter for the sake of being explicit, the GetLast() is a strawman, if i see a get last and its not a generic function or a collection i'm renaming that sucker without asking.

  • @JacobNax
    @JacobNax 15 дней назад

    I do not use nullable types with the argument that a type value should not be propagated in the first place if it is not initialized. When you are unsure whether your type values are initialized or not, you should always defer the propagation of the value to a propagator that makes sure it's propagated only when the type is valid. Personally i have found out that using value types not only is good for performance, but forces me to write eloquent code that is faultless. Ofcourse this takes more development time but i would rather put more time to sanitize my framework than null check in every single method in my code base. This is ofcourse a personal preference as using LINQ is a personal preference for most people to be more productive. Unfortunately in my work environment we are slaves to performance critical contexts we are forced to have a framework that does not allow reference types to be passed around the code base like a ping pong ball so we only work with value types that are propagated by reference type system classes. Therefor we do not have any possible null reference exceptions happening.

  • @barionlp
    @barionlp 15 дней назад

    think about which parts should be designed functional and which should be object oriented

  • @timseguine2
    @timseguine2 16 дней назад

    My usage of var is nuanced and difficult to automatically check. If the specific type itself corresponds to your expectation of its function at that point in the code write the typename. If it is incidental, write var. I also prefer `Typename varname = new()` initializers to `var varname = Typename()` but there is not a particular reason behind that
    My reasoning is this: Whenever you spell a typename out in a statically typed language it should be for the purpose of expressing an expected constraint on the behavior of the program to the compiler and the reader of the code.
    What is "incidental" in this case is up for debate I guess. But in my opinion, rules worth following are never black and white.
    This is engineering. There is always a tradeoff. Pretending like there isn't with one size fits all guidelines is never helpful.
    That's the distilled version. But at the end of the day, unless I am specifically working on performance I write whichever is most legible in that context. If spelling out an ugly typename detracts from legibility, then it is going away. End of story.

  • @Drachencheat
    @Drachencheat 15 дней назад

    We dont use nullable at work because the external workers just will never use it

  • @Esgarpen
    @Esgarpen 15 дней назад

    I embrace null by throwing exceptions, I do not return null from a method, I throw exception and catch it in e.g., the controller and return appropriate BadRequest(ex.Message) or whatever is suitable. Still, (almost) all my methods will check stuff for null because someone else will forget and there will be an object reference error which is going to cause me many grey hairs... Yell at me all you want regarding performance and what not, but I disagree that throwing an exception is the largest bottleneck in your codebase. If you are optimizing for exceptions, then your codebase gotta be hella fast everywhere else. But yeah, that's my hottake on c# :)
    -- Edit, or as another commentor states, if you are returning a collection (such as IEnumerable) then I will return Enumerable.Empty or Array.Empty etc. No need for exception there

    • @lordmetzgermeister
      @lordmetzgermeister 15 дней назад

      Honestly, I often do the same.
      If you write code properly, you should get two kinds of code: flow control and execution.
      Executing code doesn't care what happens next; if it fails, it will just end, i.e. silently end (return something neutral) or throw exception.
      Flow control would handle execution errors and decide whether it can continue or return to higher level control flow with a return value or a higher level exception.
      Note that I won't always use exceptions and also in more complex scenarios flow control can be mixed into execution.

  • @Siddiskongen
    @Siddiskongen 15 дней назад

    "If you're not handling null in a place, that's a place null can blow in your face"

    • @djp_video
      @djp_video 15 дней назад

      Conversely, any time you declare something as non-nullable and instantiate a dummy object just to avoid a warning, finding the situations where you have dummy rather than real data is a horrendous problem because no exception is thrown. I see this a lot.

  • @twiksify
    @twiksify 10 дней назад +1

    1. Don't throw, instead return Try from langext.
    2. Don't use out, instead return Option from langext.
    3. Implement interfaces explicitly to minimize a bloated API.
    4. Always test public methods, before writing production code.
    5. Avoid premature abstractions, refactor when needed.
    6. Analyzers are your friend, you can even implement your own CodeFixProvider.
    7. Have fun, curiosity drives knowledge.

  • @ApacheGamingUK
    @ApacheGamingUK 15 дней назад +1

    I think the Bovril is falling off the TDD gravy train now, thankfully. Not a single dev ever follows the "Three Laws", not even Uncle Bob. The three laws are fatally flawed, and can never produce any form of production worthy code. Every dev will always break the laws, every time they try TDD, both in Chicago School, and London School styles. Even to the point that we draw diagrams and explain TDD to people with the broken laws highlighted with a massive blue circle. It's the most ridiculous thing I've ever seen. For junior devs, I would say to do a basic Level 1 course in Business Management. Within the first few days of the course, you will be introduced to one of the most ubiquitous, and foundational concepts within the entire of business. PDSA. Plan, Do, Study, Act. If you understand PDSA, then you don't need to read a single word that Uncle Bob has ever written, and you can throw the "three laws of TDD" in the bin where they belong.

  • @abdiel_hd
    @abdiel_hd 14 дней назад

    Please make a video about var 10:34

  • @siposz
    @siposz 15 дней назад

    6:31
    Oh, we have chunk now? How long we are waiting for this?

    • @7th_CAV_Trooper
      @7th_CAV_Trooper 15 дней назад

      It's been around l since at least dotnet 6.

  • @Masterworgenn
    @Masterworgenn 16 дней назад +2

    I came to a new workplace and have been working here for a week. My colleagues have disabled nullables in all projects. I still don’t understand why they did this, but in all the code they write, they are really confident they will get results. To me, this is absurd, and I've already managed to shoot myself in the foot when I started working with such code.

    • @djp_video
      @djp_video 15 дней назад +2

      When you understand nulls, nullable reference types are an annoyance if not burden. I see so many bugs in code with nullable references enabled because developers instantiate a dummy object just to make the errors go away. It's better to get a null reference exception when you don't have the data you need than to operate on fake, dummy data because the developer chose to instantiate a fake object just to avoid a null check.

  • @f1nn21345
    @f1nn21345 3 дня назад

    I mean.. I think you were missing the point of the ambiguous examples. The user is trying to make it clear that they are referring specifically to the use of 'var'.
    - var myVar = new StringBuilder(); is clear and non-ambiguous because the type is obvious from the context.
    - var myVar = GetLast(); is more ambiguous because it's not immediately clear what type GetLast() returns just by looking at the code. The users takeaway here is that using 'var' can sometimes obscure the type information, making the code harder to read and understand.
    The user's takeaway here is that using 'var' can sometimes obscure the type information, making the code harder to read and understand. Naming, while important in any language, is a separate point from what the user was trying to convey about the clarity of type information. Claiming it is a bad example due to variable naming overlooks the users point about type ambiguity that their example clearly demonstrates.

  • @xeakpress2718
    @xeakpress2718 14 дней назад

    Im not using nullable references because idk what that is

  • @dand4485
    @dand4485 15 дней назад +2

    I'm not a fan of the nullable types. We might have internal variables in our classes, as such i'll interrogate/scrub them when assigning or setter methods. So while i'd completely agree with checking for null, elevating the nullable checks as C# does make stuff annoying. No problem with garbage in POOOF! But if there are guards up when setting/using internal variables, and i am pretty generous where i might encounter a null to code for it as needed.

  • @chrise202
    @chrise202 16 дней назад +4

    Best Practices:
    1. Best code = No code.
    2. Hire technical PMs / BAs / Scrum / Leads / QAs, with a computer science degree
    3. Have well written user stories with clear ACs
    4. CI/CD MUST BE FAST!!!!1111.
    5. Stop building Docker images for each environment. Build Once. Parameterize per environment.
    6. Give developers access to be able to do their work efficiently.
    7. Configure one click setup to run your projects locally.
    8. Don't split without decoupling. Decouple first, then split.
    9. Fail fast. In code and in SDLC. Figuring out that Sonar is unhappy after 50mins of running the CI/CD is a no-no.
    10. Use semantic versioning for Figma / Sketchup whatever.
    11. Don't take frontend developers too seriously, the HR department neglected rule nr. 2.

  • @SirBenJamin_
    @SirBenJamin_ 15 дней назад

    I really hate 'var'. The only time I use it is if the type is already mentioned (i.e reduce redundancy) or for long multiple level generic types, else I'll use the explicit type. I hate code like "var something = GetTheThing()"

    • @djp_video
      @djp_video 15 дней назад

      One of the developers I used to work with used var for EVERYTHING. Her code takes so long to debug because you can't tell what types anything is.

    • @VincentvandenBraken
      @VincentvandenBraken 15 дней назад +1

      I really hate explicit types, the name of the functions and variable names already give me all the context I need

    • @SirBenJamin_
      @SirBenJamin_ 15 дней назад

      @@VincentvandenBraken so wrong. Especially with code reviews.

  • @kaiserbergin
    @kaiserbergin 16 дней назад +4

    Use var everywhere except when you need someone to review your pull request and understand the types in a git diff.

    • @djp_video
      @djp_video 15 дней назад

      Explicit types make the code far more readable. 'var' is nearly always a mistake.

  • @smathlax
    @smathlax 16 дней назад +5

    The advice about "is" vs "==" seems kind of backwards to me. They say use "is" in case someone might override "==", but the one case where someone did override "==" (Unity), we use "==".
    So just use "==" as that clearly covers all cases.

  • @user-zk5ym9ut1j
    @user-zk5ym9ut1j 14 дней назад +2

    Nullable reference types are really dumb and make more pain then value. They don't actually exist, so you are just fooling yourself.

  • @sasukesarutobi3862
    @sasukesarutobi3862 16 дней назад +2

    I feel like a lot of people discuss "var" usage in the same way as variable naming -- in terms of code readability -- but what gets missed is the more important point of type safety even when making changes. Part of the point of not using var is so that if there's a change somewhere else in the codebase that propagates to that variable declaration, the compiler can catch it and flag it up to you. I know it can be frustrating when you're having to make a lot of changes in various scattered methods when you've changed a return type somewhere, but to me that's probably as much a smell that you might not actually want to change an existing method's return, but instead create a new method that returns in the new type.

    • @diadetediotedio6918
      @diadetediotedio6918 16 дней назад +1

      ["Part of the point of not using var is so that if there's a change somewhere else in the codebase that propagates to that variable declaration, the compiler can catch it and flag it up to you."]
      This is a fallacy, because if your variable is used then it will be errored when something else in the codebase changes. Also, I worked on pretty large software and if you keep good decoupling standards using 'var' is a no problem. Many languages use type-inferred variables as a first way of declaring things and don't suffer from this as well (all funcional languages, rust, kotlin, scala, etc).

    • @okmarshall
      @okmarshall 16 дней назад +2

      This is poor advice. Stuff still breaks with var. It's not like dynamic, it still compiles to a concrete type so it'll break if you change the return type elsewhere.

    • @smathlax
      @smathlax 16 дней назад

      @@okmarshall Unless the type that you change to is very similar to the old one.
      For example, if I change a return type from AmericanCustomer to BritishCustomer, and both have a Pay method, then no compiler error will occur when we invoke customer.Pay(). Personally I would prefer for this to be flagged as an error so that I can verify that the code is still valid. For example, maybe BritishCustomer needs an additional check before paying due to different laws or something. If the compiler doesn't flag this for me I might forget about that.
      If the code was still valid either way, it's really not a big deal to change these types manually. I hit compile, my IDE gives me a list of errors, I double click on each, and replace the type. Even if you've got 20 such errors it won't take more than 1 or 2 minutes. And indeed if you do have so many errors you should probably verify manually that each of them is still valid.

    • @epiphaner
      @epiphaner 16 дней назад +2

      @@okmarshall If something returned type Person before and now returns type Company, they might both still have the property Name. If that's the only field you use, you'll get no compile time errors, but it might still lead to problems at runtime. Having the error because of a type mismatch is a great way to know you need to inspect that bit of code.
      This gets even worse when working with databases. Almost every type will have an ID property, if you use that in your queries without explicitly stating the type you might get some real headscratchers. I'm talking from experience on that last one 😅

    • @diadetediotedio6918
      @diadetediotedio6918 15 дней назад

      @@smathlax
      If what you said happens, it is much more because of your code depending upon implicitness of passing values to functions than because the 'var' per se. If your code was not totally inlined here and you are using explicit functions to control what you are doing, then it would caught the change to BritishCustomer.
      Oh, and know what's funny? Having methods with the same names on different classes is also a form of subtle implicitness that can propagate in other ways, but I think this is not a problem for you.

  • @KingOfBlades27
    @KingOfBlades27 16 дней назад +2

    Personally I don't like the nullable settings in projects because that then requires checks everywhere and bloats classes with pointless keywords 😂

  • @MZZenyl
    @MZZenyl 15 дней назад

    Fair point regarding dynamic being a "never" rather than a "last resort", I mostly wrote that because I frankly don't know if there are any archaic edge cases where it would actually be the lesser of two evils.
    But yeah, dynamic is the devil. Had to help refactor a project that needlessly used dynamic all over the place, made me completely disavow dynamic before I was done with my morning coffee.

  • @T___Brown
    @T___Brown 15 дней назад +1

    Nick. What do you think of the Linq Any() and MS message to use > 0?

  • @PajakTheBlind
    @PajakTheBlind 16 дней назад

    I would love for people to adhere to the following: don't blindly follow conventions - especially Microsoft naming conventions. Adding Async/Exception to a name does not make it more readable and it can invite laziness in naming. Put some effort into it. If exception class name looses it's meaning after you remove the 'Exception' - the name could use more work.
    Also I wish we stuck to tabs insted of converting them to spaces. It's a character that is used to organize text and it's worth it to use it as such

    • @nieldy79
      @nieldy79 15 дней назад +1

      Agree with Async, but I think an exception class would be confusing with the Exception suffix

  • @Tsunami14
    @Tsunami14 16 дней назад +1

    Embrace the NuGet ecosystem.

    • @orterves
      @orterves 16 дней назад

      I still wish paket was used instead though

    • @Tsunami14
      @Tsunami14 16 дней назад

      @@orterves Never heard of paket, so I'll have to look that up.
      What makes it stand out from nuget?

    • @orterves
      @orterves 16 дней назад

      @@Tsunami14 transitive and grouped and centralised dependency management

  • @keyser456
    @keyser456 15 дней назад

    I was just asking AI about chunk functionality the other day. I didn't realize they added it to Linq in .NET 6. I went back to that conversation and asked it directly if Linq has a chunk method and it quite confidently replied "No". I told it I just found it in .NET 6 and it ... apologized. Does it learn from these mistakes? I downvoted the "No" answer. I assume they feed positive and negative responses to improve the model over time?

    • @victor1882
      @victor1882 15 дней назад +3

      You're using LLMs for the wrong things, you can't believe what it generates because it just outputs what "seems like" an answer, but seeming like it and being the answer are two very different things. And it's by design, that's how the tech works, try using it for more creative workloads (like poems, songs, translations etc.) and less for things that need precision and truth like code, math, searches and so on

    • @keyser456
      @keyser456 15 дней назад

      @@victor1882 I'm actually having some very productive conversations, and I love that they are saved w/ full context to be continued at a moments notice. The conversation prior to it (the one I came back to) was about a strategy I was going to use for chunking. I asked it for an example in C#. I started in .NET 1.x circa 2003 or so. I've worked on many teams and many projects. I can honestly say AI is at least as good as having a senior dev sitting next to you to ping ideas off of. It gets things wrong sometimes, but guess what? So do developers, even senior level. I've worked alongside many over the years. The point is spit-balling ideas like proofs of concept or snippets on approaches to problems is what AI excels at in the software dev arena. Because it remembers the entire discussion, you can pick up right where you left off, days or even weeks later, whereas humans would need time to read through the notes and recollect the situation. You're short-changing yourself if you're not having those types of iterative discussions w/ AI.

    • @ghaf222
      @ghaf222 15 дней назад +1

      The problem with AI at the moment is you have to then go and fact check every single thing it says.

    • @keyser456
      @keyser456 15 дней назад

      @@ghaf222 True. That's why I would never look to it as a copy/paste sort of thing. It's an iterative back and forth conversation with ideas and approaches followed up by searching and verifying, just as you'd do with advice/feedback from fellow devs.
      The difference is AI doesn't cost $80-120k a year. That's important for small startups that can't afford multiple dev salaries. This is like $10 / month to have the equivalent of a senior dev sitting next to you to bounce ideas off of. If you use it right, yes, it can be that valuable. It will not and should not be viewed as something to crank out production-ready code, imo.

  • @redon638
    @redon638 16 дней назад +1

    first

    • @okmarshall
      @okmarshall 16 дней назад +2

      .FirstOrDefault()

  • @mariacobretti
    @mariacobretti 16 дней назад +7

    overriding == should be bad pracitce imo

    • @caunt.official
      @caunt.official 15 дней назад

      Then what would be solution for my custom Enum implementation where I need few fields for each constant value? Usually I create a class with my static values and overriding == to match the properties behind each value.

    • @mariacobretti
      @mariacobretti 15 дней назад

      @@caunt.official having a static equals method that takes two of your custom enums as params would be the simplest solution.. As for value types you're most likely to check for null with hasValue anyway so here it might even be ok

    • @lepingouindefeu
      @lepingouindefeu 15 дней назад +2

      Overriding any operator is hardly justified.

    • @Sander-Brilman
      @Sander-Brilman 15 дней назад

      @@lepingouindefeu DateTime is a perfect example of a good reason imo

    • @diadetediotedio6918
      @diadetediotedio6918 15 дней назад +2

      This is bs, nothing exists without a reason and for many cases it does make sense.

  • @djp_video
    @djp_video 15 дней назад +2

    I personally hate nullable reference types. I think they are abomination and are doing a disservice to developers and are hiding rather than fixing bugs. This feature is allegedly there to help to avoid null reference exceptions. I'd maintain that while it can do that, it actually causes bigger issues in the long term. Kicking the can down the road, so to speak.
    First, it tries to make reference types act more like value types. And by doing so, it diminishes or hides some of the advantages of reference types, like for example not allocating memory for an object whose data you don't have yet. Or having a clear indicator that an object or property doesn't apply. Yes, you can absolutely manually declare a type as nullable to allow nulls, but that's not the default behavior any longer. And unless you declare a type as nullable, it CAN'T be null. So new developers who don't know better, or are coming from other languages which treat reference types in the traditional way, or developers who are lazy or not thinking when creating their declarations and forget to add the ? to the end of the type are inadvertently at least sometimes allocating memory for objects that they'll never use when it is instantiated, just to avoid the warnings. Wasting resources, in other words, both memory and CPU, needlessly. And where do many application performance or scaling issues come from? Poor management of resources. The Garbage Collector in C# can't tell ahead of time that an object instantiated at the creation of a form or page, for example, is going to be replaced when the page/form is loaded, so it allocates memory that's never going to be used. Not a great idea.
    Along those same lines, if someone simply doesn't want to deal with the ? and associated null checks, they'll just lazily instantiate one to satisfy the checks, and there's no standard, trivial way to know that the object you're about to reference in your code doesn't have the data that you anticipate that it will. It's instantiated with all of its properties having default values, and those values might be perfectly valid data, so it can sometimes be hard to know whether the data you need has been populated or not. The only way to know is to already be familiar with the object model and have enough domain knowledge to know that "hey, when property colorid is 0, then you know that you don't have good data." It actually complicates things quite a bit.
    Nulls are incredibly useful in helping you to know that the data you intend to work with isn't available, whether it be because it doesn't apply or that it hasn't been generated/retrieved yet. Any null object tells you right away that you don't have your data yet. That's what the null exception is all about. A null exception is a very clear way of letting you know, "hey, that data that you thought you loaded? Well, you didn't. You've got a bug that you need to fix." The newer nullable reference types might avoid the exception, but now you've just kicked the can down the road... you might not have loaded the data that you thought you did, but are rather operating on that dummy object that was instantiated just to get rid of the warning... but the application isn't going to generate an error to tell you that; it's going to proceed down its happy path and run the code on bogus data. It won't be until much later that you find out that you've got a bug in your code specifically because no exception is being thrown.
    People tend to look at exceptions as bad things. While we should write code that avoids them where possible, an exception tells you definitively that you've got a bug in the code. It's easy to find at that point. Anything a language does to hide them artificially like nullable reference types is covering up, rather than fixing, a bug. It's really good to know that the data you anticipate having available actually isn't. That's harder to do with nullable reference types unless explicitly declared as nullable, an extra step that is easy to miss.
    My last big complaint is that it teaches developers bad habits with reference types. By making a reference type look like its behavior is similar to a value type, developers who don't know better assume that they *act* like value types. I couldn't tell you how often I see bugs in developers' code because they make an assumption that objects are passed by value... that they can modify the properties of an object in a method and that that won't affect the object passed into the method. Hiding the fact that a variable is a reference type just adds to this confusion. I couldn't tell you how many times I've had to explain the difference to developers who have been working in the industry for years, specifically because their first exposure is in C# with nullable references enabled. The two data types are different, and they should be treated as different. Attempting to make one behave more like the other just confuses the matter and leads to bugs that could easily be avoided if people understood the differences.
    We should be encouraging developers to take the few minutes of their time to learn how reference and value types are different, not attempting to hide the differences. Each one has its own advantages and disadvantages, and I can tell you that, without a doubt, developers who start working in C# with nullable reference types enabled have a far harder time comprehending the difference between the two than developers who have either worked in other languages without this 'feature" or have been working in C# long enough to have come from an era when it wasn't available. We're doing them a disservice by trying to hide the very nature of reference types. Reference types are really cool, we shouldn't be diluting their usefulness.
    For me personally, I always turn the feature off in every one of my own projects. And I add _#nullable__ disable_ to the top of every library file that I create. And I can promise you that null reference exceptions are just not a thing in my code. Doing null checks is not hard. I don't understand why people fight it so much. It's a lot easier than checking for other invalid values. (Is ID 0 allowed or not? There's no obvious way to know.) When developers are lazy and don't declare an object as nullable, everywhere that there would have been a null check in the more traditional approach should now have a different check in place to make sure that the object contains valid data, so you really haven't saved anything; in fact you've made it worse because the check that needs to be performed is now not obvious, and made troubleshooting that much harder when those checks are omitted and no exception is thrown when the data you have isn't real.
    I despise the fact that nullable references are the default for new projects. I believe that our code is so much worse off with this enabled than we were before it was introduced. At least we could easily tell where the problems were. It's so much harder now. Having worked heavily in both environments, I see a lot more, much harder to find, issues in newer code than I ever did using the old way.
    The bottom line is that the feature discourages the use of nulls. But nulls are an incredibly powerful concept. Knowing without a doubt that data doesn't exist, or doesn't apply, is tremendously valuable. I don't believe we should be doing anything to encourage developers to avoid them or send a message that they're bad. It's a huge mistake!

  • @AlexParaVoz
    @AlexParaVoz 16 дней назад +5

    Worst thing that happen to me in c# is inheritance and developers that use it

    • @JacobNax
      @JacobNax 15 дней назад

      Agreed.. There is always composition that people tend to ignore its existance in the first place.
      Composition > Inheritance

    • @keyser456
      @keyser456 15 дней назад

      It can be and is misused, but that can be said for almost any feature in a language. At any rate the "is a" vs. "has a" is important to think about in class design. There are many cases where something really "is a" and inheritance or at least interface patterns are the preferred choice.

    • @djp_video
      @djp_video 15 дней назад

      Inheritance is a tremendously powerful concept and saves a ton of time when used correctly. Some people overuse it, but many avoid it when in some situations it could make their code far simpler.

  • @brucerosner3547
    @brucerosner3547 15 дней назад +1

    You skated around the issues that have plagued C++ for years: adding features to C++ have actually degraded the language. What actually happens with the C++ standards committee, comprised largely of very smart people who don't have enough productive work to do, is that they add more and more complex features of little worth. Psychologically they are unable to face the fact that their thousands of hours of work are a waste of time. Something similar is at work at Microsoft. How can the Microsoft C# development group justify their jobs (and high salaries) without continually adding features to C#. We have reached the point of diminishing returns in C# language features long ago, why not simply freeze C#? Naturally that would limit the market for video tutorials of "See the new C# feature in version XX"?

    • @7th_CAV_Trooper
      @7th_CAV_Trooper 15 дней назад +1

      You think they add compiler features to support the tutorial industry? LOL

  • @Biker322
    @Biker322 15 дней назад +1

    I hate that term 'best practice', generally means do it my way, and if you don't or disagree you are not following "Best Practice"

  • @carlosleon6526
    @carlosleon6526 15 дней назад

    On another note, is dometrain down ? 🥲