Get the source code for this video for FREE → the-dotnet-weekly.ck.page/smart-enums Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
@trongphan6197 Жыл бұрын
is it simplier ?
@MilanJovanovicTech Жыл бұрын
@@trongphan6197 What exactly?
@janedoe6182 Жыл бұрын
10:12 CreateEnumerations() method is overhead. You can push each enum member to the dictionary in Enumeration constructor
@MilanJovanovicTech Жыл бұрын
And initialize it how many times?
@janedoe6182 Жыл бұрын
@@MilanJovanovicTech Once. Each time you create new enum member constructor add it to dictionary - once for each member
@alfflasymphonyx10 ай бұрын
@@janedoe6182 That's what I thought as well.
@MikhailKolobovGamedevForge Жыл бұрын
Seems like solution which solve a problem thas doesn't need to be solved. If you need more than just an enum, use classes or structures.
@vincentvega5104 Жыл бұрын
He is using classes:)
@MikhailKolobovGamedevForge Жыл бұрын
@@vincentvega5104 Okay... "...use classes which not pretend to be an enums" With all this extra implementing interfaces and useless methods.
@MilanJovanovicTech Жыл бұрын
As Vincent pointed out, I am using classes 😅 With a few sprinkles on top to achieve an "enum"-like design. What's so wrong about this approach?
@MikhailKolobovGamedevForge Жыл бұрын
@@MilanJovanovicTech so why exactrly do you need to achieve enum-like behavior? If your objects doents fits in enum, don't use it like enum) Otherwise it's just unnesessary complexity. Good for youtube, but bad for real projects)
@JLPA42 Жыл бұрын
We have been using smart enums in production for some years now. I can say that it has been a joy to refactor some areas of our domain and push behaviour into our types (smart enums).
@mrogalski Жыл бұрын
Okay, I get it that all companies are trying to obfuscate the code even though it is compiled but this ... this seems like a perfect example of overengineering. There's basically zero profit from your solution and it only adds huge overhead on the "enumeration". This discount example could be simplified with proper enum and extension methods or simple attributes. Both mentioned methods gives no overhead, no additional abstraction layers, no additional class files.. I really tried to find any reason for this but I can't. It's impossible to justify this approach
Жыл бұрын
Exactly my thought. I have tried to use Ardalis.SmartEnum, which is similar and I just cannot justify it. Enums are quite powerful because they are so simple. All I can see with SmartEnums is that they remove all the benefits, and adds complexity back. If simple static classes, extension methods and attributes is not enough, then what you want is probably not an enum in the first place.
@MilanJovanovicTech Жыл бұрын
So OOP always overengineering then, Mateusz? Because I don't think the example is that complicated really. I see this as a way to achieve an enum-like design, while being able to introduce behavior in form of data/methods on the class. Extension methods are an option, but then your logic lives in a different file from the enum. And you have the cognitive load of maintaining two files when you want to add a new enum value. With my approach, everything is in one file. And you can have abstract member, which *forces* you to define said member when adding new implementations.
@mrogalski Жыл бұрын
@@MilanJovanovicTech Never said it was complicated. What I said is that is adds a ton of overhead compared to enums. Besides that, having additional functionality like a discount based on enumeration is a separate logic itself so based on SOLID principles it should be separated into its own functional class/file/method. You could potentially create enum with extension methods in one file and it would still be more readable, better maintainable and efficient. Not to mention how it could break the whole application because of the possibility to add the enumeration with the same name more than once. The issue will not be prompted during design, code or compile phase but during runtime. Adding all this makes a huge impact on performance when you're handling thousands of requests per second. I understand that the new trend in software development is to create more and more code not worrying about performance but attitude like this is the main reason we now have a simple communicators, notepads or other relatively compact applications requiring almost a GB of ram and possibly taking a ridiculous amount of CPU time. Promoting this kind of behaviors in my opinion is not the way we should follow. We should write efficient but clean and understandable code that would immediately inform us about the issues it can have.
@thomasreasoner6253 Жыл бұрын
I think you may be missing the point, which is understandable if you've never come across the situations where this sort of thing is really convenient. Also, this really has very little overhead to the point where I don't think you're using that term correctly. It also doesn't have that much abstraction: it's a fairly straightforward generic class that uses the "Curiously Recurring Template Pattern". I wrote something almost identical to this years ago, and the goal was to eliminate magic strings and to properly associate C# types with the enumerations you commonly see in database tables. Most databases will have multiple tables that define enumerations. If you read a value of '1' from some field in some record in some other table, you will typically need to have magic logic that handles the case when that value is '1' or '2' or '3', etc, and that logic will be littered with magic strings. The developer also just has to know that it effectively represents an enum. There's also the matter that each enum value represents a different kind of thing that requires different business rules to handle. You could define a vanilla enum and then try to cast those values to that enum, but that is duplicating what is already in the database, and it doesn't really improve the design and readability of the code very much. But if you use the generic enum described in this video, you can make all of the magic strings and magic logic go away, and every enum value will have a strong type with the business logic associated with that type. It can be a very convenient solution.
@mrogalski Жыл бұрын
@@thomasreasoner6253 You just described few of the possible usecases for ORM or mapping in general which is totally different from the example above. There are no "magic strings" these are types values that during runtime are treated as integers (something computers are good at). Youre trying to prove the point here that doesnt exist
@bahtiyarozdere9303 Жыл бұрын
Thanks for sharing this idea. This is a nice approach. I would still say this is a bit overkill. When you need to implement new credit card you have to go back to the CreditCard class and modify it. I believe this can be modified a bit to make it more solid friendly.
@MilanJovanovicTech Жыл бұрын
The example is trivial, so it seems overkill. But think about the concept in broader terms 😁
@oikya58049 ай бұрын
I agree..
@mylesdavies9476 Жыл бұрын
Getting a lot of stick for this video in the comments which is uncalled for. I found it interesting, thanks
@MilanJovanovicTech Жыл бұрын
I think it's part of expressing your opinions publicly. There will always be people that disagree. 😅
@PetrVejchoda Жыл бұрын
Brutal overkill, using reflections, logic coupling (credit card type is not really tightly bound to discounts - discounts could probably change overtime, and maybe based on other factors), while you can get code, with exactly the same behavior and even faster runtime creating normal enum and then creating GetDiscount extension method for it, that is a simple switch case. Much cleaner. I usualy abuse partial class for this scenarios and have one Extension class per namespace and write all stuff, that is not necesarily a part of object into a partial Extension class at the end of file.
@MilanJovanovicTech Жыл бұрын
I can never fathom why people latch onto the concrete example when trying to critique the video. From my perspective, the point here is illustrating the concept of Enumeration class. The CreditCard example can be irrelevant, but I found it simple enough so that people can follow along. Partial classes make me 🤮
@PetrVejchoda Жыл бұрын
@@MilanJovanovicTech yeah, I get your point about partial classes. And what I proposed is obviously abusing it, as I mentioned. I also get your point about latching on concrete example. Nevertheless, the point here was that you can get it much cleaner and easy with extension methods, which is exactly what they are here for. Enums are only meant for marking some stuff based on static premise (days of week). They are not supposed to be objects by themselves. They are basically meant to be a food for switch case statement. If you want to have proper classification of entity of any type, then sure, use classes. If you want for whatever reason to have only one class of each type in your app, then sure, use singletons. If you expect for whatever reason the collection of the stuff you want your classification to be extensible, you should not use Enums at all in the first place. I mean, in a way, this is a interesting pattern, and some people in the comments even found a name for it, and I am sure there is plenty use for it. I am just saying it is overengineering in plenty of cases. And many people might actually need a simpler solution for similar problem and here I am, telling them, not to follow your example.
@jfevia Жыл бұрын
I'm not sure what you call "smart enum" but this is 1) Mislabeled - there's nothing here that's either smart or enum or 2) Unnecessary and overengineered. In any case, I can assure you that this would be a no-go for anyone in our org. If anything, this is more of an abstract factory with a bit of business logic but, unless the business logic is more than just a property, you would definitely get your PR rejected trying to merge this implementation. Software engineering is already complex. There's elegance in simplicity.
@JLPA42 Жыл бұрын
I understand your view. Suggestion: take this as a tool to have in your toolbox. It might come a day where you are faced with a scenario that fits smart enums. This has already happened in some of our production projects, mainly the ones with a vast amount of business logic. We were all very pleased with the final result.
@MilanJovanovicTech Жыл бұрын
Hey Jesus, I'm sure your comment has good intentions. But do you realize what I try to do is explore concepts in present topics I find interesting? This is obviously an intentionally trivial example, that in no way represents a real use case. I believe that much is obvious. Neither am I suggesting that people go out there and replace C# enums with the Enumeration class for _every_ use case. But I had a lot of success applying this pattern in a few projects, and I find it worthwhile to share with my audience.
@jfevia Жыл бұрын
I'm sure that there are cases where such an implementation would be useful. This, IMO, isn't entirely clear in the video. It's not fixing an actual problem. A stripped down version isn't doing it any favor either. Add a bit more logic and you can no longer call that neither smart nor enum. At best, this is a controversial approach, and it shows. Unfortunately there's an inherent risk of people taking this kind of stuff and adding it to any code base (especially with this kind of exposure), thereby adding unnecessary complexity (I'm guilty of doing this myself). A better approach would be to 1) not only to explain the advantages but *also* the disadvantages of such an implementation and 2) provide a better use case or scenario. Let's not forget that we need to use the right tool for the job. Having a clear understanding of the tool is more critical than mindlessly writing code.
@kasozivincent868510 ай бұрын
Hi Milan, firstly, allow me thank you for your high quality content. I think what you are trying to do here is to discover SUM types that C# has refused to add to the language. There is a library though LanguageExt that supports them, it generates most of the code for you too. Thank you
@MilanJovanovicTech10 ай бұрын
Awesome, let me take a look 😁
@Virdues Жыл бұрын
Genuinely clear and concise video, but this seems over engineered. If you need something more than enums, use classes, which is what you did. Great implementation demonstrated, but this should have been a more "Why you should use this over enums" and give better context and real world examples.
@MilanJovanovicTech Жыл бұрын
It's tough to also come up with real-world examples in the context of 10-20min video. But I do have a use case for it in a future video, for creating and seeding Roles
@iliyan-kulishev Жыл бұрын
For most of us, this would look like unnecessary overcomplication. But great trick to have nevertheless. Thanks for the video.
@MilanJovanovicTech Жыл бұрын
I found a use for this in a few occasions. It shouldn't be used for every enum in the codebase, that's for sure.
@tomtran6936 Жыл бұрын
Woa. It's quite a good example around many practices and aspects of OOP.
@MilanJovanovicTech Жыл бұрын
Thanks
@pureevil379 Жыл бұрын
Essentially creating a way to work with classes that would inherit from a similar parent in an enum style way. I like it for adding some structure to the code base in working with inherited classes. Thanks
@MilanJovanovicTech Жыл бұрын
Glad you liked it
@TheNorthRemember Жыл бұрын
oh god I'm lost this is very complicated solution
@MilanJovanovicTech Жыл бұрын
It makes sense if you need rich behavior in your enums. If all you need is a simple enum, it's certainly too much 😅
@kwwx345 Жыл бұрын
@@MilanJovanovicTech It feels like every solution pattern needs a section for "when not to use it" because ppl always try to use it for whatever they can get their hands on.
@ApacheGamingUK Жыл бұрын
I've used a setup very similar to this to implement string-based enums, as a way to combat primitive obsessions, or "magic strings". It's also good to add implicit converters to and from strings. I'd imagine that using this with complex classes, instead of primitives, could lead to issues with polymorphism, and hierarchy, if you don't create team-wide rules as to their usage. I wonder how much of the bloat within this could be extracted via source generators, using attributes on the public static fields(/properties?) to set discount values. Personally, I don't think that an "Enum" should be used to discriminate complex information, or business logic, so a source generator that allows basic distinct information to be assigned doesn't stretch it too far. Otherwise, a dictionary wrapper, or repo would be more suitable. The thought of piling business logic into ever-more-bloated private nested classes just leaves a nasty taste in my mouth.
@MilanJovanovicTech Жыл бұрын
They don't necessarily have to be private classes, but yes, the number of classes can significantly grow. I try to not overuse this, but I find it very practical in certain situations.
@errrzarrr Жыл бұрын
Is primitive obsession that bad? The counter part is having magic numbers which is much worse than this. A Data Class is helpful tool in the end, not a sin.
@fifty-plus11 ай бұрын
We're breaking OCP here. Feels like a ValueObject lib would handle this just as well, or better.
@MilanJovanovicTech11 ай бұрын
Do we have to be strict about all the principles out there?
@phw1009 Жыл бұрын
Nice approach for rich enum type, but I would rather use CustomAttriubtes on each enums. And lazy load those informations to static dictionary, if it is necessary.
@MilanJovanovicTech Жыл бұрын
That's a cool idea. Any example out there with a similar implementation?
@benlewies88287 ай бұрын
I can see this being extended to be used in conjunction with caching and persistence, and for use with the strategy pattern.
@MilanJovanovicTech7 ай бұрын
Strategy pattern is the perfect use case
@chrissimpson6088 Жыл бұрын
In my mind, the real benefit of rich enums (in other languages) is that they’re value semantic. That is, a performant way to represent complex data structures. As soon as you use classes you’re using the heap, and to boot you’re using reflection. Kind of defeats the point IMO.
@MilanJovanovicTech Жыл бұрын
The reflection only runs once per type, so no real performance hit there
@Soraphis91 Жыл бұрын
@chris simpson but you're not creating new references. There are a few startup allocations on the heap, but they will live for the whole applications lifetime. There is no GC pressure coming from them. As already answered the Reflection is only called once per type, but you could also just add it to the dictionary in the constructor.
@angelldark6426 Жыл бұрын
i will never finish my project, you keep giving some new information, i keep updating my project. Please let me finish my project))))
@MilanJovanovicTech Жыл бұрын
Don't overengineer it!
@mrsajjad30 Жыл бұрын
Subscribed. I liked the way you explain things in a easy manner.
@MilanJovanovicTech Жыл бұрын
Thanks a lot, I have some interesting videos coming in the next few weeks 😁
@mrsajjad30 Жыл бұрын
@@MilanJovanovicTech looking forward to that.
@antonmartyniuk Жыл бұрын
It's a very interesting concept by the way. Wish C# could have something similar out of the box like some sort of smart enums exist in Java
@MilanJovanovicTech Жыл бұрын
We're not that lucky 😁
@user-ul7pm1tb5x6 ай бұрын
You're really good author and I watch your videos every day:) But this article is a little overhead. In this case I would prefer an extension method for this Enum. If I would afraid to forget implement one of case after change enum I'll write some test to check that all defined enum values are hanled into my extesions. But I agree with you in case when we need to many consistent logic per each enum value. Good luck!
@MilanJovanovicTech6 ай бұрын
Note that this is the strategy pattern, pretty much. With a way to make it look like an enum. It has its uses.
@ryan-heath Жыл бұрын
Though the implementation is nice, it is YAGNI for most of the time. There is a reason we have simple enum types. 😉
@MilanJovanovicTech Жыл бұрын
There's also a situation where simple enums aren't enough
@harryh212 Жыл бұрын
I prefer to create a custom attribute where I can assign data to my enum members
@MilanJovanovicTech Жыл бұрын
Interesting
@CodeBallast Жыл бұрын
How about just assigning the Discount property with a getter only from a private constructor? Thereby you don't need three different classes.
@davidwilliss5555 Жыл бұрын
That was going to be my suggestion. Just make the constructor for CreditCard take a discount value. Then you don't even need the inherited classes.
@MilanJovanovicTech Жыл бұрын
That's an option of course, I just wanted to show the approach with an abstract member. Probably would have made more sense with an abstract method with some logic.
@theagemaway11 ай бұрын
I really like the idea behind all of this but i think it could be better achieved using records to gain most of the functionality with less code (or rather, the compiler generates the code for you with records). For me, a simple: public record CreditCard{ Public string Name; Public int Value; Public double Discount; Public static readonly CreditCard Normal => new(1, "Normal", 0.01); // other types as necessary } This type of simple implementation is plenty enough for me, and small enough to fit into one file.
@MilanJovanovicTech11 ай бұрын
Might have to revisit this idea with records :)
@user-rm7hn4ur7e10 ай бұрын
This way the principle of OCP is violate!
@alexmadnix Жыл бұрын
Thanks for this video!
@MilanJovanovicTech Жыл бұрын
My pleasure!
@henrikcarlsen1881 Жыл бұрын
In my eyes you crossed the river to get water, this being merely a coding exercise. Think the implemtation could be much simpler.
@MilanJovanovicTech Жыл бұрын
Perhaps, when you take this example into consideration. But a rich enum is very useful in some places, I think it's a nice tool to know about.
@chrix_app Жыл бұрын
Or use Ardalis.SmartEnum
@allannielsen4752 Жыл бұрын
Excellent library and includes all the serialisation people are commenting on.
@MilanJovanovicTech Жыл бұрын
Indeed, great option
@ShyamSundar055 Жыл бұрын
Well explained 👏 thanks
@MilanJovanovicTech Жыл бұрын
My pleasure 😁
@manapotion1594 Жыл бұрын
Pardon, I would not hire you because you seem to have a tendency to overengineer things
@MilanJovanovicTech Жыл бұрын
I'm in luck then, since I don't need hiring. Phew 🥵
@jadenrogers3133 Жыл бұрын
Steve Smith's SmartEnum nuget package does this.
@MilanJovanovicTech Жыл бұрын
Cool!
@5cover8 ай бұрын
I used a similar pattern but much simpler without a base class, just a sealed class with a private parameterless constructor and public static get-only properties. I think the code you've written is massively over-engineered. When it comes to adding some simple logic to an enum, using an extension method is also a solution.
@MilanJovanovicTech8 ай бұрын
The only reason it's "over-engineered" is because I'm creating something generic
@sajjadarash3295 Жыл бұрын
That nice thank you for share this❤ But we can to create thatethod create enumeration without reflection in constructore class
@MilanJovanovicTech Жыл бұрын
How?
@sajjadarash3295 Жыл бұрын
@@MilanJovanovicTech when constructor with two parameter is called add parameters to that Dictionary :D
@1Eagler10 ай бұрын
Going from NY to Boston through LA.
@MilanJovanovicTech10 ай бұрын
Did you make it?
@alexandrehando11 ай бұрын
What would be the difference between a strongly typed enum and an Value Objects?
@MilanJovanovicTech11 ай бұрын
Intent.
@adambickford8720 Жыл бұрын
Java has 'rich enums' and i giggle every time a junior dev finger-traps himself with it. Bonus points if they are also somehow represented in the database and have to be kept in sync!
@MilanJovanovicTech Жыл бұрын
I have to see what Java can do 😁
@svorskemattias Жыл бұрын
Looks like strategy pattern + a storable identifier to me. Can you enforce exhaustive pattermatching switch expressions with this?
@MilanJovanovicTech Жыл бұрын
I'm not sure about that, gotta check
@ThiagoBechara Жыл бұрын
That, kids, is a way to implements Abstract Factory Design Pattern. Great video!
@MilanJovanovicTech Жыл бұрын
That could be one way to look at it, but a few pieces are missing to make it a real factory. Don't you think?
@billacount Жыл бұрын
there are a lot of issues with this code. I tried following through and writing the code using my VS 2019 environment and it didn't work on many different things first when I tried to follow your convention of removing the name space curly braces namespace SmartEnum{} and replace it with SmartEnum; that failed. when I tried using protected init; I was unable to When I tried to use TENum? it also threw an exception I think these are all configuration issues. but you should address them before starting the video so that we can follow along and use your code thanks for the idea but can you provide some help in rectifying these issues?
@MilanJovanovicTech Жыл бұрын
Use .NET 6 or .NET 7
@gabdemello9 ай бұрын
Cool solution. I am now having difficulty implementing it in my API. I'm using auto mapper and some abstractions like repository and unit of work. The scenario is as follows: The customer can have two subscription plans, the pro and the free plan. This is reported via json: {"ClientName":"john Doe", "Subscription":"Free"}. If anyone can help me, please adapt the logic would be incredible.
@MilanJovanovicTech9 ай бұрын
Parse on controller level?
@gabdemello9 ай бұрын
@@MilanJovanovicTech Exactly. Actually, I'll try to be clearer. I have a client model that has the 'subscription' attribute, and this attribute is an intelligent enum (it applies what was shown in the video). My challenge is adapting its logic with serialization (JSON) and AutoMapper (DTO). This is because to create the attribute, it would be like this: 'var subscription = SubscriptionPlans.FromName("Free");' I need to call the 'FromName' method, so I probably need to make changes in AutoMapper or serialization, right? I'm not sure if I managed to be clear, I hope so. Thank you for your time and dedication in sharing knowledge!
@bjarkeistruppedersen8213 Жыл бұрын
How would you do this, so it still works with the Flags attribute a normal enum can use?
@MilanJovanovicTech Жыл бұрын
Probably possible with writing some code to overload the | operator. But I'm not sure how fun that is going be to implement properly 😅
@aamirali81149 ай бұрын
From Where do you learn all these things can share us link or book name. Like i cant even imagine to write a code in such a beautiful way. please help us to acknowledge from where one should practise such great learnings like you share with us
@MilanJovanovicTech9 ай бұрын
I don't know, really. Lots of sources. Pluralsight courses. Clean coding book. Refactoring book.
@rasimismatulin1400 Жыл бұрын
1. You can add transitions. For example Status enums. ( New -> Confirm, Edit, Delete; Confirm -> Delete; and so on) I found it useful in doc processing to get next Status transitions via one line of code in several places (doc.Status.GetNextStatuses();) 2. Serialization/Deserialization "musthave" 3. Localization for enums also can be abstract CreditCard.LocalizedName. 4. Add support EFCore or Dapper. I didn't find best solution, so in Entity model I have int property and cast it by AutoMapper domain.Status.FromValue(entity.Status). But I have projection error with this solution. Maybe there are more elegant way
@MilanJovanovicTech Жыл бұрын
4. EF Value Converter is what I use, works great
@robby.roboter Жыл бұрын
Just use Dictionary
@MilanJovanovicTech Жыл бұрын
What will that bring us?
@robby.roboter Жыл бұрын
@@MilanJovanovicTech less code, same result credit[premium] will give discount for premium
@thomasreasoner6253 Жыл бұрын
I wrote almost this exact thing 5 years ago. I'm a little shocked how similar this code is to mine. Is this a common idea or pattern that I'm not aware of?
@MilanJovanovicTech Жыл бұрын
It's a common idea
@RobertPaulsim Жыл бұрын
ugh. code horror. when a bug comes, and people look at this type of code they panic. some things are better in KISS mode, IMHO.
@MilanJovanovicTech Жыл бұрын
What is horror code here exactly? 🤔
@benlewies88287 ай бұрын
I think I may be missing something, but the static Enumerator variable will be overwritten in memory each time you call a different child class of Enumerator?
@MilanJovanovicTech7 ай бұрын
Each generic class is a different instance, so the static variable shouldn't be affected
@margosdesarian5 ай бұрын
How does this compare with Ardalis SmartEnum approach?
@MilanJovanovicTech5 ай бұрын
Pretty similar
@jegtugado3743 Жыл бұрын
I only see abstraction and inheritance. The only thing close to an enum is that you have a static instance for each credit card type and the equality was changed to value and not reference. Maybe you can list some good real world examples on when to use this and not with a credit card?
@MilanJovanovicTech Жыл бұрын
Try to look beyond the credit card example, and when you could benefit from a custom Enumeration. I've had a few use cases where I needed behavior on top of an enum, usually in the form of some method that computes something.
@emadali1906 Жыл бұрын
Is this an implementation of the strategy pattern ?
@MilanJovanovicTech Жыл бұрын
In a way...
@AboutCleanCode Жыл бұрын
Watched the video a second time and even though there is a bit of code needed "as infrastructure" (the base class) as well as for the implementation of a concrete use case I definitively like the idea of "avoiding bugs by design". Looking at the controversial discussions in the comments I wonder whether some missed the key achievement: the logic is no longer decoupled from the enum values. Means, as you stated in your introduction, with the initial implementation one could forget to update the switch statement when a new enum value is added. With the new approach you just add another abstract method/property and once everything compiles again you are done and you can be sure that the rest of the software will work as expected. No need to run the full regression test suite of the entire software system. this is "fail fast" to the extreme. I think this benefit is worth some extra code. Personally I would favor a more "functional programming approach" over an OOP approach which would look like this: kzbin.info/www/bejne/f6C4pKx7rryKZs0
@MilanJovanovicTech Жыл бұрын
Yes! You get the point. I was surprised by the outburst of comments on this video 😅 I wonder if we combine my approach with Match extension method we get something really nice 🤔
@AboutCleanCode Жыл бұрын
@@MilanJovanovicTech sounds like "Smart Enums - Part 2" ;-)
@mkwpaul Жыл бұрын
I appreciate what you're trying to achieve. But all this code feels like compensating for the lack of certain language features. (Namely algebraic types and analysis of switches for completeness). And what you end up with is a mountain of boilerplate and less performant, more complicated code. Less performant because virtual function calls are slower than non-virtual ones, because dictionary lookups are slower than just using value literals and iterating over the values (in the FromName method) is even slower. The static dictionary and static fields also increase your base memory footprint, and generating the dictionary via Reflection increases startup, even if Reflection is way faster than people give it credit for (at least in C#). I know that performance isn't the be-all and end-all of programming and that the performance cost of this is a non-issue in 99.9% of cases, but it still irks me the wrong way cause one could have these abstractions and safety guarantees without any performance cost and without the boilerplate and complexity. I am also generally not a fan of bundling behavior together with the data. Theoretically one could even have it in C# if they'd spend the time to write custom code analyzers and enforced analyzer errors on build, which C# thankfully has build in. Good Video nonetheless.
@MilanJovanovicTech Жыл бұрын
You must not be a fan of DDD also 🤔
@mkwpaul Жыл бұрын
@@MilanJovanovicTech I love DDD, I think it's essential for any larger code base. I am just not too big a fan of OO, because I believe it to be very cumbersome and complex, with weak tools for abstraction. Abstractions you need to model your domain. The reason I care about the performance cost even though it practically doesn't matter is that better solutions already exist elsewhere and we could have them in C#.
@waleed-alshinawi Жыл бұрын
Thank you Milan for the video, really interesting topic and a good tool to have in any developer's toolbox. I wanted to ask if you can make a video on test the Domain layer in Domain Driven Design, I'm have so much trouble testing my domain models (aggregates / entities) since the domain model doesn't have setters (private) and the constructor is also hidden, IFixture didn't help me and i had to write long tests and with time tests are becoming more and more harder to write thanks a lot and really appreciate your efforts
@MilanJovanovicTech Жыл бұрын
For creating a new instance, you need to expose something. Either a constructor, or a static method. You don't need to test setters, but behavior. And you can do that with methods.
@dxs1971 Жыл бұрын
@Sam Verify -> If you want to test Domain Object it needs to have constructor (or public Create method) Then in test project you can create builder pattern for it
@MarcusKaseder Жыл бұрын
There is one additional advantage you didn't mention in that video. Let's say, you have a project that should be highly extendable. For example, you have got a base project with your common code. And you have a variation of your base project for Customer B that needs more Enum values. With a normal Enum, you can't extend the origin Enum in the customer B project. It's just not possible, since the Enum lives in your common project. There are some possibilities with (int) parsing but that's just bad practice. With the "smart" Enum approach, you can just add a CustomerBCreditCard : CreditCard class, add additional public static readonly Enum values for CustomerB like "public static readonly CreditCard Diamond" and everything will just work fine. Even the reflection dictionary will collect the new additional values and parsing will just work as before. Just wanted to mention this "huge" additional advantage. Of course, you need to add that kind of support but it's easy.
@MilanJovanovicTech Жыл бұрын
That's a very interesting extensibility option, it hasn't crossed my mind
@richardhaughton9633 Жыл бұрын
Looks good, would've been nice to add a solution for EFCore and Json Serialization/Deserialization which is a major paint point with those kind of implementations
@MilanJovanovicTech Жыл бұрын
If all the data is static, it can be enough to store the Value integer with value converters
@MilanJovanovicTech Жыл бұрын
Can be a separate video 👌
@saeedrezataheri2578 Жыл бұрын
Please Send source code link please!
@MilanJovanovicTech Жыл бұрын
I share the source code on PAtreon
@SeCluDred Жыл бұрын
I smushed subscribe button so hard that now it is stuck inside UI :D
@MilanJovanovicTech Жыл бұрын
Is that why I got 100 subscribers out of nowhere? You really smashed it good 😂
@mjunior7717 ай бұрын
Maybe a better solution would be to use the abstract factory design pattern?
@MilanJovanovicTech7 ай бұрын
This is one way to implement something similar. I see it more as the strategy pattern
@nanvlad Жыл бұрын
Not sure why would I need this, especially with int backing field as magic number, but I'm wondering if you tried to use the new 'static abstract' keywords for a card value
@MilanJovanovicTech Жыл бұрын
How static abstract on interfaces help here?
@eyupcalis9275 Жыл бұрын
Nice video. i will use this enumeration class. Thank you 😊
@MilanJovanovicTech Жыл бұрын
Thanks, glad you liked it!
@errrzarrr Жыл бұрын
-1 You never moved Premium, Platinum, etc strings to a Enum -2 In C# we already have a classes (data classes) and structs (which effectively are classes in practice)
@MilanJovanovicTech Жыл бұрын
I guess I did -1 and -2 combined
@perfectionbox Жыл бұрын
Hmmm so it's a Factory pattern that returns subclasses given an enum value. People already do that.
@MilanJovanovicTech Жыл бұрын
Who said they didn't? 🤔
@alamir2020 Жыл бұрын
In a solution. Is it better to keep all enums in one project?
@MilanJovanovicTech Жыл бұрын
I try to be pragmatic about it, define them where I need them
@MB-Kajtech Жыл бұрын
I tend to have them in Utils project that should not have any dependencies in the solution. But of course if you have enums that are only used in a single project then you should have them in that project.
@alamir2020 Жыл бұрын
@@MilanJovanovicTech in enterprise. I think having them in one repository will make it easy to locate them for developers right? I am getting confuse for implementing the best practice. Thanks
@nghianguyen170192 Жыл бұрын
I found this over engineered just for enum. Basically, enum has all out of the box methods for such cases and it violates some of OOP which is composition over inheritance.
@MilanJovanovicTech Жыл бұрын
Fair enough, I'm here to show ideas. You decide what works and doesn't work for you :)
@craigmunday37074 ай бұрын
I found the video present some useful ideas for storing additional data with the enum, which I have needed to do on a number of occasions.
@amirsolhi3150 Жыл бұрын
Good job, You can take care of JsonSerialization/Deserialization as a suggestion. (The official implementation supports that). Maybe EFCore default mapping is another concern too.
@MilanJovanovicTech Жыл бұрын
I usually map only the value for EF Core, although mapping the entire object helps with some queries.
@reagang8038 Жыл бұрын
Excellent video. Thanks for sharing this
@MilanJovanovicTech Жыл бұрын
Thanks, glad you liked it! 😁
@hamidhos8101 Жыл бұрын
nice, where is code address?
@MilanJovanovicTech Жыл бұрын
You can get the code on my Patreon, but there are also some examples on my GitHub
@mihaikanyaro3460 Жыл бұрын
Or use SmartEnum
@MilanJovanovicTech Жыл бұрын
Excellent library 👌
@sawek111 Жыл бұрын
Great feature, I used similar for value objects from limited range of possibilities. Milan, do You have any tips for vertical slice architecture in Clean Architecture, I am thinking about it... Should I slice it in both core layers separately? How to divide mutual operations for only couple of many slices? I would be really grateful for advices :)
@MilanJovanovicTech Жыл бұрын
Could you give me a more concrete example? Jimmy Boggard has a great video on the topic
@sawek111 Жыл бұрын
@Milan Jovanović I will watch Jimmy's Boggard video and maybe find my answer there without bothering You. In other cases I will address you with concrete examples. Thanks a million:)
@rafaelscheffer1262 Жыл бұрын
great video, thanks for sharing
@MilanJovanovicTech Жыл бұрын
Thanks, Rafael. Glad you liked it
@porcinetdu6944 Жыл бұрын
That is a massive like
@MilanJovanovicTech Жыл бұрын
Glad you liked it 😁
@fernandocalmet Жыл бұрын
Very good strategy to have Enums with behavior. This goes perfectly for DDD architectures. Thanks for sharing Milan :)
@MilanJovanovicTech Жыл бұрын
I've used in DDD typed applications mostly
@inayelle Жыл бұрын
The implementation would be even cooler with .NET 7 and abstract static methods thingy ;)
@MilanJovanovicTech Жыл бұрын
I have to check that out, honestly
@majormartintibor Жыл бұрын
Nice video, thanks for this.
@MilanJovanovicTech Жыл бұрын
You're among the few that enjoyed it. It seems I triggered a lot of people 😂
@majormartintibor Жыл бұрын
@@MilanJovanovicTech I have a software in my regular job where I might just refactor based on this. There is so much business logic based on an enum, that I think it would make the code cleaner.
@MilanJovanovicTech Жыл бұрын
@@majormartintibor Just weigh it carefully against the increase in complexity
@Lammot Жыл бұрын
eShopOnContainers is a gold mine, eh? :> Now do a serialization, deserialization and db storage of those enums. Just to feel real cost of implementation.
@MilanJovanovicTech Жыл бұрын
Just store the Enumeration value with a value converter. Same storage space as an enum.
@Lammot Жыл бұрын
@@MilanJovanovicTech That's the point. You've traded 8 lines of code for 2 abstract clases and 3 concrete ones, plus a need for custom converters for what is usually a trivial operation. Don't get me wrong, looks cool, but in reality it's very niche because of the implementation effort.
@MilanJovanovicTech Жыл бұрын
@@Lammot It makes sense if you have a lot of logic. This isn't an enum replacement for the simplest use case
@Lammot Жыл бұрын
@@MilanJovanovicTech can't argue with that. :)
@dcp15121980 Жыл бұрын
How do I unsubscribe if I smash the subscribe button.
@MilanJovanovicTech Жыл бұрын
You can't, you're subscribed forever 🤷♂
@HagobSaldadianSmeik Жыл бұрын
No, I think this is really bad. You took a simple enum and turned it into something more like an abstract factory. Stuff like that would be the first thing marked for refactoring in a code review in my company. In this example, keep the actual enum, add an extension method for the enum that creates the credit card classes. Less code, easily understandable by junior engineers and most importantly it requires no inheritance.
@MilanJovanovicTech Жыл бұрын
Well, if I saw extension methods instead of nice, slick, design, I would mark that for refactoring. There, we're even now 😁
@nikitafrolov7668 Жыл бұрын
Imho: it's better to use ctor to fill static dictionary, instead of using reflection.
@necrokora7688 Жыл бұрын
Ctor gets called for every instantiation. In Milans case (with it the Enumeration field being static) the CreateEnumerations Method only gets called once at application startup, where reflection of this size has little to no perf impact. You would also be in danger of forgetting to add new instances in the ctor if you did it your way.
@MilanJovanovicTech Жыл бұрын
@@necrokora7688 💯
@benlewies88287 ай бұрын
@@necrokora7688 Would this be on application startup or on first use of a concrete implementation of Enumerator?
@T___Brown Жыл бұрын
Enums are an antipattern. Using data can better describe and is scalable. It doesnt destroy api interfaces like enums do.
@MilanJovanovicTech Жыл бұрын
Enums are now an antipattern? 😁 Tomorrow writing code will be an antipattern.
@T___Brown Жыл бұрын
@@MilanJovanovicTech i consider them antipattern when writing api code. I dont consider them antipattern for closed applications.
@benlewies88287 ай бұрын
Who, apart from yourself, consider them antipatterns? Can you make a proper case for this point of view?
@T___Brown7 ай бұрын
@@benlewies8828 its only for api code. When you make a change to your api to add/remove/modify an enum and you tell your customers this is an enum. Then they are bound to a specific set of values. If they havent changed their code then they will get an error when parsing. This is extremely bad if you are maintaining systems where you dont want to affect your customers. Also, it doesnt work well if you are versioning unless you have a specific enum for every version which is cumbersome. It is better to just use a text value of the enum.
@dakotapearl0 Жыл бұрын
It seems like you're trying to invent something a bit like a discriminated union.
@MilanJovanovicTech Жыл бұрын
I'm not inventing anything, Jimmy Bogard made this popular
@fenkusinghАй бұрын
dont kill enums for class instance garbage, you wasted lots of memory and created performance issue. let ENUM type be just ENUM. NO need to create videos on everything ...
@MilanJovanovicTechАй бұрын
Enums + behavior
@serhiyskaletsky9095 Жыл бұрын
Thanks for the video! Some thoughts about using reflection to get our defined enum values, since we have a very limited amount of instances,, so in this case, in the base enumeration class we can add each instance to the dictionary (we need to use downcast like (TEnum)this but since we have type constraints, we can consider this cast as safe ) and the only problem we still have - static fields is not initialized before we call the actual type, it could be solved with System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(TEnum).TypeHandle); in the base static constructor I'm not pretending that it's a better solution, just as an alternative to reflection which is not always available...
@MilanJovanovicTech Жыл бұрын
I think any issue around that can be easily found and fixed