Тёмный

Hacking C#: Development for the Truly Lazy - Simon Painter - NDC Copenhagen 2022 

NDC Conferences
Подписаться 194 тыс.
Просмотров 17 тыс.
50% 1

I don't know about you, but I'm a lazy developer. What do I mean by lazy? I don't mean I don't want to do my work - far from it - I mean that I hate to write out a great deal of code to get the job done. I want to accomplish my goals with as little effort as possible.
One of my pet hates is writing enhancements that involve copying and pasting blocks of code, changing a variable name, then leaving everything else the same. I hate having to consider each and every possible null reference exception, and adding in a whole ton of boilerplate to handle it. I hate having to spent ages jumping back and forth in a legacy codebase, trying to understand what it actually does!
What's the alternative? In this talk, I'll demonstrate a way of working that avoids all this unneccesary work, and gives you more time to do something more productive.
We'll look at:
* Functional Programming - what benefits does this increasingly popular paradigm bring us to cut down coding effort
* Linq & Generics - These have been a part of C# for a long time now, and are some of the most powerful features available in the language, but hardly anyone seems to be using them effectively
* MetaProgramming - break open C# and take it to the next level with code that describes how to generate code
Our goal is to write code in as few lines as possible that provides the greatest amount of impact. We also want code that's readable, and easily maintainable. We want to think smart, and think...Lazy.
Check out more of our featured speakers and talks at
www.ndcconferences.com
ndccopenhagen.com/

Наука

Опубликовано:

 

15 июл 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 84   
@tordjarv3802
@tordjarv3802 Год назад
Maybe I'm to much of a physicist but I think that 9*tempCentigrade/5 + 32 is more readable than anything presented in this video.
@pagorbunov
@pagorbunov Год назад
You need to abstract cause it was just a simple sample.
@simonpainter2242
@simonpainter2242 Год назад
In the real world, I probably would do something like that, especially as it doesn't really matter for this one example. I just didn't want to get too bogged down in real-world detail when I was talking through the example.
@tordjarv3802
@tordjarv3802 Год назад
@@pagorbunov while it is true that using other forms of chaining operations together in more complicated situations is better, this was a talk about being a lazy programmer so making an example where the end result is more complicated than the starting point kind of defeats the purpose of the example.
@tordjarv3802
@tordjarv3802 Год назад
@@simonpainter2242 It is ok. I really enjoyed your talk. I particularly liked your approach to add default values in case of missing values.
@simonpainter2242
@simonpainter2242 Год назад
@@tordjarv3802 That's a good point. I'll have a think about that particular example for the next iteration of this talk, Thanks :)
@redpepper74
@redpepper74 Год назад
After learning Java and Python for a year or so and picking up some Haskell, C# looks like the best of both worlds when it comes to OOP and functional. Extension methods seem great for general-purpose libraries, and LINQ looks like straight-up good declarative programming. It’s exciting seeing all the stuff .NET can be used for, can’t wait to get started.
@simonpainter2242
@simonpainter2242 Год назад
I like it. LINQ is one of C#'s very best features. Along with Pattern Matching and record types. I hope you enjoy working with it!
@akitelesforo
@akitelesforo Год назад
Great talk, appreciate how you play c#, beauty of linq
@simonpainter2242
@simonpainter2242 Год назад
Thanks!!!
@tejeshreddy6252
@tejeshreddy6252 Год назад
This was an interesting presentation, Simon. Although I don't agree with a lot of your points, as some other commenters have pointed out, I appreciate the effort you've put into this. I also appreciate the civility you've displayed to bad faith comments, it is often hard to be unfazed by trolls in the comments. Kudos to you!
@simonpainter2242
@simonpainter2242 Год назад
Thanks. At the very least I hope I was entertaining :)
@ConductiveFoam
@ConductiveFoam Год назад
This was very interesting, if not for the specific extensions then for the approach. Thinking I'll be able to use this for some of the legacy stuff I maintain for my day job actually.. Thanks a bunch! I've got an extension I like quite a bit: Extracting the non-null elements in an enumerable public static IEnumerable NonNull(this IEnumerable source) => source.Where(e => e is not null).Select(e => e!)
@simonpainter2242
@simonpainter2242 Год назад
nice, I like that one. It's not a problem I encounter often, but I can see it being useful.
@CRBarchager
@CRBarchager Год назад
13:53 Shouldn't the second line of map be x => x / 5 ?
@larsthomasdenstad9082
@larsthomasdenstad9082 Год назад
Good catch. I hope your oven baked duck turned out OK.
@prouleau4440
@prouleau4440 Год назад
For me, that demonstrate the signal-to·noise ratio of that monad is terrible. The classic math formula on 1 line is way simpler and everyone learned to read and write that at primary school. Beside that, many interesting tricks.
@simonpainter2242
@simonpainter2242 Год назад
@@prouleau4440 Thanks. Perhaps I should swap the example at some point. I went with the temperature conversion as the simplest example I could think of, since I didn't want to get too bogged down in real-world detail.
@simonpainter2242
@simonpainter2242 Год назад
Thanks. I've had an email about that too. I've been doing this talk for years and no-one's ever noticed that mistake until today!
@rastislavsvoboda4363
@rastislavsvoboda4363 Год назад
@@simonpainter2242 couple of years ago I've used in real app: public static class DialService { public static bool DialTel(string number) { return number .Map(ToRawNumber) .Map(ToTelUrl) .Map(UIApplication.SharedApplication.OpenUrl); } public static bool DialSkype(string number) { return number .Map(ToRawNumber) .Map(ToSkypeUrl) .Map(UIApplication.SharedApplication.OpenUrl); } private static string ToRawNumber(string number) { return new StringBuilder(number) .Replace(" ", string.Empty) .Replace("-", string.Empty) .Replace("(", string.Empty) .Replace(")", string.Empty) .ToString() .Trim(); } private static NSUrl ToSkypeUrl(string rawNumber) { return NSUrl.FromString($"skype:{rawNumber}?call"); } private static NSUrl ToTelUrl(string rawNumber) { return NSUrl.FromString($"tel:{rawNumber}"); } }
@alexclark6777
@alexclark6777 Год назад
36:00 Is the ContainsConsecutiveNumbers going to scale? Given that it's recursive (and I'm not 100% sure but I don't think C# will do some kind of fancy tail-recursion optimization) isn't this likely to smash the stack over a certain array length? It may have been a bit easier on the heap (and probably the stack!) to just iterate and compare current to previous, stopping when either the condition has been falsified (current != previous) or verified (end of array). In fact that might make for an interesting generic function, a "private static bool ConsecutiveAll" which takes an IEnumerable and does exactly that.
@simonpainter2242
@simonpainter2242 Год назад
It likely won't scale. Sadly C# doesn't implement any of the nice tail optimised recursion functionality you get in F#. Since writing this talk (about 3 or 4 years ago) I've learned it's also possible to do some of that with use of the Zip function in LINQ as well.
@DuRoehre90210
@DuRoehre90210 Год назад
Yeah, I miss coding C#. Most of this looks familiar to my previous achievements, having a toolkit of generic LINQ extensions methods for "any purpose". Extra boosted by Resharper. Python is picking up a on a few modern C# features like string interpolation, partly better and partly worse, but similar. And yet, it remains much less smooth than C# extension methods. 24:32 I wouldn't necessarily agree. Adding ToArray or ToList ensures that the operation is finished, which might be important in case of databases (because locking). Passing IEnumerable around extends the runtime to ..
@AndersBaumann
@AndersBaumann 6 месяцев назад
ToList() is very dangerous when you are working with a large database. I have experienced out-of-memory exceptions multiple times because of a ToList. In my company we have added code that puts a limit to the number of objects in memory to be robust against a misplaced ToList.
@lawrencejob
@lawrencejob Год назад
Map deserves a deeper dive than it got in this presentation - it came across like speaker didn’t know about IEnumerable.Select but of course that’s not the case. I do think such a general use of monads defeats the point of using monads though (unless you have an agenda like handling nulls/optionals or errors) - this topic should have gotten more time imo bc I think it’s a hidden gem in this talk that probably confused people (reading comments like “Map==Select??”)
@simonpainter2242
@simonpainter2242 Год назад
I think I should make a point of explaining the difference between Map and Select the next time I do this. It's obvious to me, but I've been doing it for years, so it's easy for me to inadvertently miss out an important explanation. I didn't want to get too deep into FP and Monads in this talk - I have another in which I do a much deeper dive. I thought it might be interesting to at least touch on the subject. I probably should have chosen a better example, perhaps, though.
@omarbousbia6916
@omarbousbia6916 Год назад
indexes are a cool feature in C#, but one downside of it is that you can't do go to definition on them
@simonpainter2242
@simonpainter2242 Год назад
that's true, but when they're used in the right place, it's a small price to pay, I feel
@Robert-G
@Robert-G Год назад
dictionaries that return null on not found are only useful in specific cases, and then they will be specific wrappers around an actual dictionary. Finding null is not the same as not having the value. Putting that into the bcl is not going to happen. There’s a GetValueOrDefault on immutable diction, it might come to the Enumerable extension class at some point… btw: That’s what TryGetValue is for, tells you whether you found it, and then let you use it, without looking it up twice, as the code is doing here (contains & indexer) I thought most of the tricks are really hacks that increase runtime cost sneakily without people knowing why their server are spending a lot more time on GC sweeps. And they also make stuff more convoluted not easier to follow at all. However, there were some nice nuggets I’ll take away from it. Thanks :-)
@simonpainter2242
@simonpainter2242 Год назад
Thanks. I'm glad I was some small use :) I'd tend to use the IDictionary wrapper I proposed here when I have a default value in mind. I tend to avoid uses of Null wherever it's possible. I'm not sure I agree it makes the code more convoluted, I was trying to cut down on the amount of code on display. My reasoning is that no-one necessarily cares what's behind the boilerplate extension method. I hope I was of some use though. Thanks so much for watching :)
@Robert-G
@Robert-G Год назад
​@@simonpainter2242 dang small screens. my post read a bit more negative than intended. But I do have a bit of a default skepsis against adding stuff that allocates or is cool, vs a no-nonsense in-your-face version.
@simonpainter2242
@simonpainter2242 Год назад
@@Robert-G I can see your point & I don't disagree. Amongst my aims here though, was to disguise recurring bits of boilerplate that we're all used to using with a meaningful description. In my head, it's better for junior developers, who want to quickly understand the meaning of a codebase, but without needing to decode the meaning of every little block of C#.
@Robert-G
@Robert-G Год назад
@@simonpainter2242 I use the one where the variable part of the code can be passed as a closure all the time (you called it donut :D ). That’s perfect for reuse and also to hide code with lots of chest hair that needs to work in all kinds of scenarios, but shouldn’t be intimidating to use. Absolutely with you on that.
@simonpainter2242
@simonpainter2242 Год назад
@@Robert-G I honestly have no idea what the correct term for the Doughnut is :p I suspect it might be a "Thunk", but I'm probably wrong. It's definitely a powerful technique though & very in line with the SOLID principles.
@CRBarchager
@CRBarchager Год назад
42:30 The question section is difficult to follow along as you cannot hear the question. It would be great if the speaker would repeat the question before answering it.
@simonpainter2242
@simonpainter2242 Год назад
Apologies. I usually try and repeat the question. I must have missed this one. I'll try and be more vigilant in future!
@CRBarchager
@CRBarchager Год назад
@@simonpainter2242 Thank you for answering.
@kazimierzszadkowski1800
@kazimierzszadkowski1800 Год назад
In my opinion proposed "improvement" of ToFahrenheit is insanity. One liner would be best. Version with variables is still ok, readable and simple. Version with "Map" forces me to think what is actually going on there. Don't like it at all :(
@pagorbunov
@pagorbunov Год назад
You need to abstract cause it was just a simple sample.
@PbPomper
@PbPomper Год назад
I fully agree. Some of these proposals are not readable at all. Everyone loves small methods, but it shouldn't go at the expense of readiblity. But his most important message came across very well and I fully agree with abstracting boilerplate code by using generic extensions. Basically DRY and OCP.
@Robert-G
@Robert-G Год назад
not to mention the runtime costs are orders of magnitude above the straightforward no-nonsense version
@simonpainter2242
@simonpainter2242 Год назад
​@@PbPomper That was roughly the main principle I was trying to bring across. Most of the ideas I was presenting here were intended as inspiration for a way of thinking about C#, rather than "these are all of the extensions you need and how to use them". I didn't think readability was too bad, but that may be a personal preference thing. Always keen to hear if you've got more & better ideas though? These are just my personal time-savers that I tend to throw into new projects
@simonpainter2242
@simonpainter2242 Год назад
@@Robert-G I'm not sure they're necessarily orders of magnitude worse. I do tend to focus on brevity and readability over performance though. My reasoning is that it's easier these days to throw a bit of money at Azure than to have the developers spend longer working on an enhancement.
@mar_sze
@mar_sze Год назад
There were some nice things in this but also surprisingly many that I would consider actually pretty bad practice.
@simonpainter2242
@simonpainter2242 Год назад
which bits? I'm happy to look into any issues before I do this talk again
@mar_sze
@mar_sze Год назад
@@simonpainter2242 I think most has already been mentioned other comments, and I mostly agree with it, such as: - Recursive approaches might scale badly - Using ContainsKey + get instead of TryGetValue - Returning null for missing keys - Over-func`ing. I use funcs and extension methods a lot (and I agree with the "rules" you set up, well done!), but also we need to keep in mind they do come with a cost and sometimes might make readbility worse than improve it (e.g. the chained .Map() instead of a simple expression) Btw, I never thought of using index with params, so that was a nice little nuget. Another trick I like: in some cases you could use generics instead of a type dictionary, which will be faster: private static class Impl { public static Func? Func } Impl.Func = s => s.Trim(); Impl.Func = d => Math.Round(d); public static T Clean(T value) => Impl.Func?.Invoke(value) ?? value;
@Mr7Shane
@Mr7Shane Год назад
@@simonpainter2242 The example of "Map", what is your problem with the local variables? What do you mean "waste local variables" and "var a is wasting spacing".
@simonpainter2242
@simonpainter2242 Год назад
@@Mr7Shane they're instantiated and used for a single line, then never used again. If the function were incredibly long, they're still being held in memory until the end of the function, even though they are of no further use.
@michaelnurse9089
@michaelnurse9089 Год назад
RU-vid is buggy. I upvoted once for the talk and once for the Thundercats reference. Instead of recording two upvotes it did none. Strange.
@simonpainter2242
@simonpainter2242 Год назад
oh well, at least you enjoyed the talk, I hope?
@cccyberfamilydk
@cccyberfamilydk Год назад
Do you have a github link?
@oleggavrilov7083
@oleggavrilov7083 Год назад
So you could report and block that nonsense? Smart move mate
@simonpainter2242
@simonpainter2242 Год назад
@@oleggavrilov7083 No need to be unkind.
@oleggavrilov7083
@oleggavrilov7083 Год назад
@@simonpainter2242 please let me decide for myself
@rastislavsvoboda4363
@rastislavsvoboda4363 Год назад
10:47 this is IsNotNullOrEmpty, or did I miss something? public static bool IsNullOrEmpty(this IEnumerable @this) => !@this?.Any() ?? true;
@simonpainter2242
@simonpainter2242 Год назад
Isn't IsNullOrEmpty for Strings? The example I gave was to do the same thing with Enumerables
@rastislavsvoboda4363
@rastislavsvoboda4363 Год назад
@@simonpainter2242 I mean your code is IsNotNullOrEmpty, not IsNullOrEmpty
@simonpainter2242
@simonpainter2242 Год назад
@@rastislavsvoboda4363 I see what you mean, I'll have a look and see whether a correction is needed. Thanks!
@cverde1234
@cverde1234 Год назад
I'd like to see the like/dislike ratio on this video. There are some good points but also some mistakes and bad code.
@simonpainter2242
@simonpainter2242 Год назад
Weirdly I have twice as many likes for this video as my FP video from Porto. I have no idea how many dislikes I have (and honestly I don't think I could live with that knowledge if it were available). Hopefully it's a sign that folks are feeling generous and forgiving my faults :) That or folks *really* hate FP.
@BryonLape
@BryonLape Год назад
Developers not gathering requirements is the first big mistake.
@simonpainter2242
@simonpainter2242 Год назад
I can't disagree with you here, although in my experience, the biggest problem is the customer/business not actually *Knowing* what they want in the first place! Another reason I try to write very "low noise" code - it's easier to change if there's a proper confirmation of an unclear requirement.
@BryonLape
@BryonLape Год назад
@@simonpainter2242 Never ask a customer what they want, they rarely know, and often when they think they do, its wrong. Ask them what they want to do. What are they trying to accomplish. How does that fit into the flow of their day.
@etiennelemieux472
@etiennelemieux472 Год назад
13:57 This version is buggy. x/5 became x/9...
@simonpainter2242
@simonpainter2242 Год назад
Thanks. I'll have a look & see if I can fix the slide for next time
@etiennelemieux472
@etiennelemieux472 Год назад
@@simonpainter2242 oh glad you answered, didn't know you would read this. I found your presentation very interesting (and entertaining), with many resources and ideas to re-use.
@simonpainter2242
@simonpainter2242 Год назад
@@etiennelemieux472 thanks :) and yes, I always try and keep an eye on the comments threads.
@jalvrus
@jalvrus Год назад
Extension methods and the ability to create "fluent" APIs is one of the best things to ever happen to C#. The funny thing about that is that it was all created to serve Linq-to-SQL, which is hardly ever used. JFYI, your dictionary extensions could be more efficient: this.TryGetValue(x, out var value) ? value : defaultValue; Also, don't forget default values for parameters: ToLookupWithDefault(this, K x, V defaultValue = default) => ...
@Kefir0
@Kefir0 Год назад
> Linq-to-SQL, which is hardly ever used What do you mean, LINQ to SQL translation is used in EF / EF Core which are everywhere.
@simonpainter2242
@simonpainter2242 Год назад
Thanks. I'll have a look at the TryGetValue code ready for the next time I do this talk.
@jessemorgan8226
@jessemorgan8226 Год назад
Most of these are the exact kind of black-box 'art' that make you cringe when you have to take over some pre-existing codebase and you can see the developer has gone out of their way to implement things using the most 'intelligent' implementations they can think of, using as many syntactic bells and whistles they can find, feeling proud of themselves that they have made such masterpieces of 'elegance' - but completely failing to recognise that so much of the elegance is in their head and not the code that it is actually obfuscating the codebase and overcomplicating it for anyone but them. He even mentions that there are probably newer syntactic bells and whistles in newer C# versions and that some of his designs may be 'out of date' I am also struck by the extra number of function calls his code will be generating causing more and more stack frame setup and usage. Simpler code is not out of date because there are new idioms available in newer language versions. Simpler code is not less lines either. Simpler code is code that takes the least time to reason about to a coder who has never looked at the codebase before. Newer syntax is great for heavy lifting where it is appropriate and makes things better. Use what is fit for purpose not what is the newest.
@simonpainter2242
@simonpainter2242 Год назад
I think it's one of those things where a bit of common sense is called for. I agree that it can be taken too far, and code can end up hard to read. When I add in these sorts of extensions though, I try and make each one as small and discrete as possible, and with a clear naming convention, so hopefully no-one would even *need* to look at the code behind it. When this sort of approach is done right (easier said than done!) I'd argue that it's actually easier to reason out what the code is doing, as there's less "code noise" obscuring the intention of the code. I'm basically advocating for a declarative code style over Imperative.
@samuelschwager
@samuelschwager Год назад
Map already exists, it is called Select :P
@oleggavrilov7083
@oleggavrilov7083 Год назад
Finally
@simonpainter2242
@simonpainter2242 Год назад
Map and Select aren't the same thing. Select operates on each element of an Enumerable separately. Map operates on the entire source object. If you called Map(x => ... on an Enumerable, then x would be the Enumerable itself
Далее
Plain Text - Dylan Beattie - NDC Copenhagen 2022
59:20
Просмотров 158 тыс.
ЛУЧШАЯ ПОКУПКА ЗА 180 000 РУБЛЕЙ
28:28
50 YouTubers Fight For $1,000,000
41:27
Просмотров 110 млн
Don't throw exceptions in C#. Do this instead
18:13
Просмотров 253 тыс.
The Clean Code Talks - Don't Look For Things!
37:57
Просмотров 283 тыс.
AVOID these 3 COMMON C# Performance MISTAKES
14:20
Просмотров 9 тыс.
The weirdest way to loop in C# is also the fastest
12:55
ЗАКОПАЛ НОВЫЙ ТЕЛЕФОН!!!🎁😱
0:28