How To Create Smart Enums in C# With Rich Behavior

Ғылым және технология

Get the source code for this video for FREE → the-dotnet-weekly.ck.page/sma...
☄️ Master the Modular Monolith Architecture: bit.ly/3SXlzSt
📌 Accelerate your Clean Architecture skills: bit.ly/3PupkOJ
🚀 Support me on Patreon to access the source code: / milanjovanovic
Enums are a great way to improve readability in your code. But enums in C# have a minor problem - you can't add behavior to enum elements. You can only work with enum values. In this, I will show you how you can create a custom smart enum implementation that can contain behavior.
Join my weekly .NET newsletter:
www.milanjovanovic.tech
Read my Blog here:
www.milanjovanovic.tech/blog
Subscribe for more:
kzread.info...
Chapters
0:00 The problem with C# enums
1:16 Creating the Enumeration class
5:11 Implementing Enumeration in CreditCard
8:44 Completing Enumeration implementation
13:17 Adding logic to CreditCard enum

Пікірлер: 227

  • @MilanJovanovicTech
    @MilanJovanovicTech Жыл бұрын

    Get the source code for this video for FREE → the-dotnet-weekly.ck.page/smart-enums

  • @trongphan6197

    @trongphan6197

    Жыл бұрын

    is it simplier ?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    @@trongphan6197 What exactly?

  • @MikhailKolobovGamedevForge
    @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

    @vincentvega5104

    Жыл бұрын

    He is using classes:)

  • @MikhailKolobovGamedevForge

    @MikhailKolobovGamedevForge

    Жыл бұрын

    @@vincentvega5104 Okay... "...use classes which not pretend to be an enums" With all this extra implementing interfaces and useless methods.

  • @MilanJovanovicTech

    @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

    @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

    @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).

  • @janedoe6182
    @janedoe6182 Жыл бұрын

    10:12 CreateEnumerations() method is overhead. You can push each enum member to the dictionary in Enumeration constructor

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    And initialize it how many times?

  • @janedoe6182

    @janedoe6182

    Жыл бұрын

    ​@@MilanJovanovicTech Once. Each time you create new enum member constructor add it to dictionary - once for each member

  • @alfflasymphonyx

    @alfflasymphonyx

    8 ай бұрын

    @@janedoe6182 That's what I thought as well.

  • @bahtiyarozdere9303
    @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

    @MilanJovanovicTech

    Жыл бұрын

    The example is trivial, so it seems overkill. But think about the concept in broader terms 😁

  • @oikya5804

    @oikya5804

    7 ай бұрын

    I agree..

  • @mrogalski
    @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

    @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

    @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

    @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

    @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

  • @mrsajjad30
    @mrsajjad30 Жыл бұрын

    Subscribed. I liked the way you explain things in a easy manner.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Thanks a lot, I have some interesting videos coming in the next few weeks 😁

  • @mrsajjad30

    @mrsajjad30

    Жыл бұрын

    @@MilanJovanovicTech looking forward to that.

  • @pureevil379
    @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

    @MilanJovanovicTech

    Жыл бұрын

    Glad you liked it

  • @tomtran6936
    @tomtran6936 Жыл бұрын

    Woa. It's quite a good example around many practices and aspects of OOP.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Thanks

  • @antonmartyniuk
    @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

    @MilanJovanovicTech

    Жыл бұрын

    We're not that lucky 😁

  • @mylesdavies9476
    @mylesdavies9476 Жыл бұрын

    Getting a lot of stick for this video in the comments which is uncalled for. I found it interesting, thanks

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I think it's part of expressing your opinions publicly. There will always be people that disagree. 😅

  • @kasozivincent8685
    @kasozivincent86859 ай бұрын

    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

  • @MilanJovanovicTech

    @MilanJovanovicTech

    9 ай бұрын

    Awesome, let me take a look 😁

  • @alexmadnix
    @alexmadnix Жыл бұрын

    Thanks for this video!

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    My pleasure!

  • @ShyamSundar055
    @ShyamSundar055 Жыл бұрын

    Well explained 👏 thanks

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    My pleasure 😁

  • @ApacheGamingUK
    @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

    @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

    @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.

  • @benlewies8828
    @benlewies88285 ай бұрын

    I can see this being extended to be used in conjunction with caching and persistence, and for use with the strategy pattern.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    5 ай бұрын

    Strategy pattern is the perfect use case

  • @fifty-plus
    @fifty-plus9 ай бұрын

    We're breaking OCP here. Feels like a ValueObject lib would handle this just as well, or better.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    9 ай бұрын

    Do we have to be strict about all the principles out there?

  • @iliyan-kulishev
    @iliyan-kulishev Жыл бұрын

    For most of us, this would look like unnecessary overcomplication. But great trick to have nevertheless. Thanks for the video.

  • @MilanJovanovicTech

    @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.

  • @angelldark6426
    @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

    @MilanJovanovicTech

    Жыл бұрын

    Don't overengineer it!

  • @user-ul7pm1tb5x
    @user-ul7pm1tb5x4 ай бұрын

    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!

  • @MilanJovanovicTech

    @MilanJovanovicTech

    4 ай бұрын

    Note that this is the strategy pattern, pretty much. With a way to make it look like an enum. It has its uses.

  • @5cover
    @5cover6 ай бұрын

    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.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    6 ай бұрын

    The only reason it's "over-engineered" is because I'm creating something generic

  • @waleed-alshinawi
    @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

    @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

    @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

  • @chrissimpson6088
    @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

    @MilanJovanovicTech

    Жыл бұрын

    The reflection only runs once per type, so no real performance hit there

  • @Soraphis91

    @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.

  • @phw1009
    @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

    @MilanJovanovicTech

    Жыл бұрын

    That's a cool idea. Any example out there with a similar implementation?

  • @eyupcalis9275
    @eyupcalis9275 Жыл бұрын

    Nice video. i will use this enumeration class. Thank you 😊

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Thanks, glad you liked it!

  • @ryan-heath
    @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

    @MilanJovanovicTech

    Жыл бұрын

    There's also a situation where simple enums aren't enough

  • @sajjadarash3295
    @sajjadarash329511 ай бұрын

    That nice thank you for share this❤ But we can to create thatethod create enumeration without reflection in constructore class

  • @MilanJovanovicTech

    @MilanJovanovicTech

    11 ай бұрын

    How?

  • @sajjadarash3295

    @sajjadarash3295

    11 ай бұрын

    @@MilanJovanovicTech when constructor with two parameter is called add parameters to that Dictionary :D

  • @ThiagoBechara
    @ThiagoBechara Жыл бұрын

    That, kids, is a way to implements Abstract Factory Design Pattern. Great video!

  • @MilanJovanovicTech

    @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?

  • @AboutCleanCode
    @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: kzread.info/dash/bejne/e6OJ1dt-qbq9YM4.html

  • @MilanJovanovicTech

    @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

    @AboutCleanCode

    Жыл бұрын

    @@MilanJovanovicTech sounds like "Smart Enums - Part 2" ;-)

  • @harryh212
    @harryh212 Жыл бұрын

    I prefer to create a custom attribute where I can assign data to my enum members

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Interesting

  • @TheNorthRemember
    @TheNorthRemember Жыл бұрын

    oh god I'm lost this is very complicated solution

  • @MilanJovanovicTech

    @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

    @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.

  • @svorskemattias
    @svorskemattias Жыл бұрын

    Looks like strategy pattern + a storable identifier to me. Can you enforce exhaustive pattermatching switch expressions with this?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I'm not sure about that, gotta check

  • @reagang8038
    @reagang8038 Жыл бұрын

    Excellent video. Thanks for sharing this

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Thanks, glad you liked it! 😁

  • @theagemaway
    @theagemaway9 ай бұрын

    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.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    9 ай бұрын

    Might have to revisit this idea with records :)

  • @user-rm7hn4ur7e

    @user-rm7hn4ur7e

    8 ай бұрын

    This way the principle of OCP is violate!

  • @bjarkeistruppedersen8213
    @bjarkeistruppedersen8213 Жыл бұрын

    How would you do this, so it still works with the Flags attribute a normal enum can use?

  • @MilanJovanovicTech

    @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 😅

  • @richardhaughton9633
    @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

    @MilanJovanovicTech

    Жыл бұрын

    If all the data is static, it can be enough to store the Value integer with value converters

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Can be a separate video 👌

  • @aamirali8114
    @aamirali81147 ай бұрын

    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

  • @MilanJovanovicTech

    @MilanJovanovicTech

    7 ай бұрын

    I don't know, really. Lots of sources. Pluralsight courses. Clean coding book. Refactoring book.

  • @rasimismatulin1400
    @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

    @MilanJovanovicTech

    Жыл бұрын

    4. EF Value Converter is what I use, works great

  • @billacount
    @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

    @MilanJovanovicTech

    Жыл бұрын

    Use .NET 6 or .NET 7

  • @benlewies8828
    @benlewies88285 ай бұрын

    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?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    5 ай бұрын

    Each generic class is a different instance, so the static variable shouldn't be affected

  • @adambickford8720
    @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

    @MilanJovanovicTech

    Жыл бұрын

    I have to see what Java can do 😁

  • @jorgeutrilla9892
    @jorgeutrilla9892 Жыл бұрын

    Nice!, thanks!.🙌

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    You got it 😎

  • @gabdemello
    @gabdemello7 ай бұрын

    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.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    7 ай бұрын

    Parse on controller level?

  • @gabdemello

    @gabdemello

    7 ай бұрын

    @@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!

  • @Virdues
    @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

    @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

  • @CodeBallast
    @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

    @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

    @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.

  • @1Eagler
    @1Eagler8 ай бұрын

    Going from NY to Boston through LA.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    8 ай бұрын

    Did you make it?

  • @rafaelscheffer1262
    @rafaelscheffer1262 Жыл бұрын

    great video, thanks for sharing

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Thanks, Rafael. Glad you liked it

  • @jadenrogers3133
    @jadenrogers3133 Жыл бұрын

    Steve Smith's SmartEnum nuget package does this.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Cool!

  • @alexandrehando
    @alexandrehando9 ай бұрын

    What would be the difference between a strongly typed enum and an Value Objects?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    9 ай бұрын

    Intent.

  • @thomasreasoner6253
    @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

    @MilanJovanovicTech

    Жыл бұрын

    It's a common idea

  • @robby.roboter
    @robby.roboter Жыл бұрын

    Just use Dictionary

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    What will that bring us?

  • @robby.roboter

    @robby.roboter

    Жыл бұрын

    @@MilanJovanovicTech less code, same result credit[premium] will give discount for premium

  • @sawek111
    @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

    @MilanJovanovicTech

    Жыл бұрын

    Could you give me a more concrete example? Jimmy Boggard has a great video on the topic

  • @sawek111

    @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:)

  • @majormartintibor
    @majormartintibor Жыл бұрын

    Nice video, thanks for this.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    You're among the few that enjoyed it. It seems I triggered a lot of people 😂

  • @majormartintibor

    @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

    @MilanJovanovicTech

    Жыл бұрын

    @@majormartintibor Just weigh it carefully against the increase in complexity

  • @emadali1906
    @emadali1906 Жыл бұрын

    Is this an implementation of the strategy pattern ?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    In a way...

  • @serhiyskaletsky9095
    @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

    @MilanJovanovicTech

    Жыл бұрын

    I think any issue around that can be easily found and fixed

  • @errrzarrr

    @errrzarrr

    Жыл бұрын

    Reflection? Please no!

  • @jegtugado3743
    @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

    @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.

  • @MarcusKaseder
    @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

    @MilanJovanovicTech

    Жыл бұрын

    That's a very interesting extensibility option, it hasn't crossed my mind

  • @fernandocalmet
    @fernandocalmet Жыл бұрын

    Very good strategy to have Enums with behavior. This goes perfectly for DDD architectures. Thanks for sharing Milan :)

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I've used in DDD typed applications mostly

  • @mjunior771
    @mjunior7715 ай бұрын

    Maybe a better solution would be to use the abstract factory design pattern?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    5 ай бұрын

    This is one way to implement something similar. I see it more as the strategy pattern

  • @SeCluDred
    @SeCluDred Жыл бұрын

    I smushed subscribe button so hard that now it is stuck inside UI :D

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Is that why I got 100 subscribers out of nowhere? You really smashed it good 😂

  • @margosdesarian
    @margosdesarian3 ай бұрын

    How does this compare with Ardalis SmartEnum approach?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    3 ай бұрын

    Pretty similar

  • @inayelle1044
    @inayelle1044 Жыл бұрын

    The implementation would be even cooler with .NET 7 and abstract static methods thingy ;)

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I have to check that out, honestly

  • @perfectionbox
    @perfectionbox Жыл бұрын

    Hmmm so it's a Factory pattern that returns subclasses given an enum value. People already do that.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Who said they didn't? 🤔

  • @PetrVejchoda
    @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

    @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

    @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.

  • @errrzarrr
    @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

    @MilanJovanovicTech

    Жыл бұрын

    I guess I did -1 and -2 combined

  • @henrikcarlsen1881
    @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

    @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.

  • @mkwpaul
    @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

    @MilanJovanovicTech

    Жыл бұрын

    You must not be a fan of DDD also 🤔

  • @mkwpaul

    @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#.

  • @alamir2020
    @alamir2020 Жыл бұрын

    In a solution. Is it better to keep all enums in one project?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I try to be pragmatic about it, define them where I need them

  • @MB-Kajtech

    @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

    @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

  • @nanvlad
    @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

    @MilanJovanovicTech

    Жыл бұрын

    How static abstract on interfaces help here?

  • @amirsolhi3150
    @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

    @MilanJovanovicTech

    Жыл бұрын

    I usually map only the value for EF Core, although mapping the entire object helps with some queries.

  • @porcinetdu6944
    @porcinetdu6944 Жыл бұрын

    That is a massive like

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Glad you liked it 😁

  • @nghianguyen170192
    @nghianguyen17019210 ай бұрын

    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

    @MilanJovanovicTech

    10 ай бұрын

    Fair enough, I'm here to show ideas. You decide what works and doesn't work for you :)

  • @craigmunday3707

    @craigmunday3707

    2 ай бұрын

    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.

  • @hamidhos8101
    @hamidhos8101 Жыл бұрын

    nice, where is code address?

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    You can get the code on my Patreon, but there are also some examples on my GitHub

  • @saeedrezataheri2578
    @saeedrezataheri2578 Жыл бұрын

    Please Send source code link please!

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I share the source code on PAtreon

  • @RobertPaulsim
    @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

    @MilanJovanovicTech

    Жыл бұрын

    What is horror code here exactly? 🤔

  • @Lammot
    @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

    @MilanJovanovicTech

    Жыл бұрын

    Just store the Enumeration value with a value converter. Same storage space as an enum.

  • @Lammot

    @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

    @MilanJovanovicTech

    Жыл бұрын

    @@Lammot It makes sense if you have a lot of logic. This isn't an enum replacement for the simplest use case

  • @Lammot

    @Lammot

    Жыл бұрын

    @@MilanJovanovicTech can't argue with that. :)

  • @jfevia
    @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

    @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

    @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

    @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.

  • @nikitafrolov7668
    @nikitafrolov7668 Жыл бұрын

    Imho: it's better to use ctor to fill static dictionary, instead of using reflection.

  • @necrokora7688

    @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

    @MilanJovanovicTech

    Жыл бұрын

    @@necrokora7688 💯

  • @benlewies8828

    @benlewies8828

    5 ай бұрын

    @@necrokora7688 Would this be on application startup or on first use of a concrete implementation of Enumerator?

  • @T___Brown
    @T___Brown Жыл бұрын

    Enums are an antipattern. Using data can better describe and is scalable. It doesnt destroy api interfaces like enums do.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Enums are now an antipattern? 😁 Tomorrow writing code will be an antipattern.

  • @T___Brown

    @T___Brown

    Жыл бұрын

    @@MilanJovanovicTech i consider them antipattern when writing api code. I dont consider them antipattern for closed applications.

  • @benlewies8828

    @benlewies8828

    5 ай бұрын

    Who, apart from yourself, consider them antipatterns? Can you make a proper case for this point of view?

  • @T___Brown

    @T___Brown

    5 ай бұрын

    @@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.

  • @dcp15121980
    @dcp15121980 Жыл бұрын

    How do I unsubscribe if I smash the subscribe button.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    You can't, you're subscribed forever 🤷‍♂

  • @mihaikanyaro3460
    @mihaikanyaro3460 Жыл бұрын

    Or use SmartEnum

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Excellent library 👌

  • @manapotion1594
    @manapotion1594 Жыл бұрын

    Pardon, I would not hire you because you seem to have a tendency to overengineer things

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I'm in luck then, since I don't need hiring. Phew 🥵

  • @dakotapearl0
    @dakotapearl0 Жыл бұрын

    It seems like you're trying to invent something a bit like a discriminated union.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    I'm not inventing anything, Jimmy Bogard made this popular

  • @HagobSaldadianSmeik
    @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

    @MilanJovanovicTech

    Жыл бұрын

    Well, if I saw extension methods instead of nice, slick, design, I would mark that for refactoring. There, we're even now 😁

  • @usmanfarooq3071
    @usmanfarooq30716 ай бұрын

    Too much gymnastics...

  • @MilanJovanovicTech

    @MilanJovanovicTech

    6 ай бұрын

    Ok

  • @fenkusingh
    @fenkusingh2 күн бұрын

    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

    @MilanJovanovicTech

    Күн бұрын

    Enums + behavior

  • @chrix_app
    @chrix_app Жыл бұрын

    Or use Ardalis.SmartEnum

  • @allannielsen4752

    @allannielsen4752

    Жыл бұрын

    Excellent library and includes all the serialisation people are commenting on.

  • @MilanJovanovicTech

    @MilanJovanovicTech

    Жыл бұрын

    Indeed, great option

Келесі