How To Create Smart Enums in C# With Rich Behavior

แชร์
ฝัง
  • เผยแพร่เมื่อ 27 พ.ย. 2024

ความคิดเห็น • 231

  • @MilanJovanovicTech
    @MilanJovanovicTech  2 ปีที่แล้ว +7

    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

  • @janedoe6182
    @janedoe6182 2 ปีที่แล้ว +9

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      And initialize it how many times?

    • @janedoe6182
      @janedoe6182 2 ปีที่แล้ว +5

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

    • @alfflasymphonyx
      @alfflasymphonyx ปีที่แล้ว

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

  • @mrogalski
    @mrogalski 2 ปีที่แล้ว +33

    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

    •  2 ปีที่แล้ว +6

      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  2 ปีที่แล้ว +3

      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 2 ปีที่แล้ว +16

      @@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 ปีที่แล้ว +1

      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 ปีที่แล้ว +2

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

  • @MikhailKolobovGamedevForge
    @MikhailKolobovGamedevForge 2 ปีที่แล้ว +72

    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 2 ปีที่แล้ว +2

      He is using classes:)

    • @MikhailKolobovGamedevForge
      @MikhailKolobovGamedevForge 2 ปีที่แล้ว +5

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +8

      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 2 ปีที่แล้ว +23

      @@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 2 ปีที่แล้ว +20

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

  • @bahtiyarozdere9303
    @bahtiyarozdere9303 2 ปีที่แล้ว +20

    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  2 ปีที่แล้ว +7

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

    • @oikya5804
      @oikya5804 ปีที่แล้ว

      I agree..

  • @mylesdavies9476
    @mylesdavies9476 2 ปีที่แล้ว +2

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

  • @pureevil379
    @pureevil379 ปีที่แล้ว +1

    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

  • @kasozivincent8685
    @kasozivincent8685 ปีที่แล้ว +1

    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

  • @tomtran6936
    @tomtran6936 2 ปีที่แล้ว +2

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

  • @Virdues
    @Virdues 2 ปีที่แล้ว +3

    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  2 ปีที่แล้ว +1

      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

  • @ApacheGamingUK
    @ApacheGamingUK 2 ปีที่แล้ว +4

    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  2 ปีที่แล้ว

      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 2 ปีที่แล้ว +2

      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.

  • @tapetedepadaria
    @tapetedepadaria หลายเดือนก่อน

    This is awesome! I was thinking about this for some days, but this seems to be the closest to the java's enums! Will try it today :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  หลายเดือนก่อน

      Go for it! There's also a NuGet package that does this

  • @iliyan-kulishev
    @iliyan-kulishev 2 ปีที่แล้ว +7

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      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.

  • @TheNorthRemember
    @TheNorthRemember 2 ปีที่แล้ว +7

    oh god I'm lost this is very complicated solution

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      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 2 ปีที่แล้ว +6

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

  • @fifty-plus
    @fifty-plus ปีที่แล้ว +2

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

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

  • @ryan-heath
    @ryan-heath 2 ปีที่แล้ว +3

    Though the implementation is nice, it is YAGNI for most of the time.
    There is a reason we have simple enum types. 😉

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

  • @chrismsimpson
    @chrismsimpson 2 ปีที่แล้ว +4

    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  2 ปีที่แล้ว

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

    • @Soraphis91
      @Soraphis91 2 ปีที่แล้ว +2

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

  • @jfevia
    @jfevia 2 ปีที่แล้ว +8

    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 2 ปีที่แล้ว +1

      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  2 ปีที่แล้ว

      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 2 ปีที่แล้ว +3

      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.

  • @angelldark6426
    @angelldark6426 ปีที่แล้ว +1

    i will never finish my project, you keep giving some new information, i keep updating my project. Please let me finish my project))))

  • @mrsajjad30
    @mrsajjad30 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

    • @mrsajjad30
      @mrsajjad30 2 ปีที่แล้ว

      @@MilanJovanovicTech looking forward to that.

  • @PetrVejchoda
    @PetrVejchoda 2 ปีที่แล้ว +10

    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  2 ปีที่แล้ว +1

      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 2 ปีที่แล้ว +4

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

  • @МаксимВеснин-и6э
    @МаксимВеснин-и6э 9 หลายเดือนก่อน

    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  9 หลายเดือนก่อน

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

  • @antonmartyniuk
    @antonmartyniuk ปีที่แล้ว +1

    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

  • @harryh212
    @harryh212 ปีที่แล้ว +4

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

  • @robby.roboter
    @robby.roboter 2 ปีที่แล้ว +1

    Just use Dictionary

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      What will that bring us?

    • @robby.roboter
      @robby.roboter 2 ปีที่แล้ว

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

  • @ThiagoBechara
    @ThiagoBechara 2 ปีที่แล้ว +3

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      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?

  • @benlewies8828
    @benlewies8828 10 หลายเดือนก่อน +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  10 หลายเดือนก่อน

      Strategy pattern is the perfect use case

  • @5cover
    @5cover 11 หลายเดือนก่อน +1

    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  11 หลายเดือนก่อน

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

  • @theagemaway
    @theagemaway ปีที่แล้ว +1

    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.

  • @alexmadnix
    @alexmadnix ปีที่แล้ว +1

    Thanks for this video!

  • @adambickford8720
    @adambickford8720 2 ปีที่แล้ว +1

    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!

  • @phw1009
    @phw1009 ปีที่แล้ว +2

    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?

  • @ShyamSundar055
    @ShyamSundar055 2 ปีที่แล้ว +1

    Well explained 👏 thanks

  • @henrikcarlsen1881
    @henrikcarlsen1881 2 ปีที่แล้ว +4

    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  2 ปีที่แล้ว

      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.

  • @CodeBallast
    @CodeBallast 2 ปีที่แล้ว +4

    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 2 ปีที่แล้ว +2

      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  2 ปีที่แล้ว +1

      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.

  • @K1llRay64
    @K1llRay64 12 วันที่ผ่านมา

    Hey Milan
    How to map types derived from smart Enum to DTOs and how to compose the DTO itself to avoid duplicating these static fields?
    What practice would you recommend?

    • @MilanJovanovicTech
      @MilanJovanovicTech  11 วันที่ผ่านมา

      You could just return the Id or just the Name? Both are enough to create an instance

  • @AboutCleanCode
    @AboutCleanCode 2 ปีที่แล้ว +1

    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: th-cam.com/video/IoUsyEyWW0k/w-d-xo.html

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      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 2 ปีที่แล้ว +2

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

  • @sajjadarash3295
    @sajjadarash3295 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  ปีที่แล้ว

      How?

    • @sajjadarash3295
      @sajjadarash3295 ปีที่แล้ว

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

  • @1Eagler
    @1Eagler ปีที่แล้ว

    Going from NY to Boston through LA.

  • @rasimismatulin1400
    @rasimismatulin1400 2 ปีที่แล้ว +2

    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  2 ปีที่แล้ว

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

  • @mkwpaul
    @mkwpaul 2 ปีที่แล้ว +2

    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  2 ปีที่แล้ว

      You must not be a fan of DDD also 🤔

    • @mkwpaul
      @mkwpaul 2 ปีที่แล้ว +1

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

  • @eyupcalis9275
    @eyupcalis9275 2 ปีที่แล้ว +1

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

  • @aamirali8114
    @aamirali8114 ปีที่แล้ว +1

    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  ปีที่แล้ว

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

  • @svorskemattias
    @svorskemattias ปีที่แล้ว +1

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

  • @richardhaughton9633
    @richardhaughton9633 2 ปีที่แล้ว +3

    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  2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +4

      Can be a separate video 👌

  • @jadenrogers3133
    @jadenrogers3133 ปีที่แล้ว +1

    Steve Smith's SmartEnum nuget package does this.

  • @alexandrehando
    @alexandrehando ปีที่แล้ว +1

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

  • @RobertPaulsim
    @RobertPaulsim 2 ปีที่แล้ว +4

    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.

  • @thomasreasoner6253
    @thomasreasoner6253 ปีที่แล้ว +1

    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?

  • @errrzarrr
    @errrzarrr 2 ปีที่แล้ว +3

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

  • @billacount
    @billacount 2 ปีที่แล้ว +1

    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?

  • @jegtugado3743
    @jegtugado3743 2 ปีที่แล้ว +1

    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  2 ปีที่แล้ว

      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.

  • @bjarkeistruppedersen8213
    @bjarkeistruppedersen8213 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      Probably possible with writing some code to overload the | operator. But I'm not sure how fun that is going be to implement properly 😅

  • @benlewies8828
    @benlewies8828 10 หลายเดือนก่อน

    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  10 หลายเดือนก่อน

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

  • @waleed-alshinawi
    @waleed-alshinawi 2 ปีที่แล้ว +1

    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  2 ปีที่แล้ว

      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 2 ปีที่แล้ว

      @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

  • @nghianguyen170192
    @nghianguyen170192 ปีที่แล้ว +1

    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  ปีที่แล้ว

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

    • @craigmunday3707
      @craigmunday3707 7 หลายเดือนก่อน

      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.

  • @MarcusKaseder
    @MarcusKaseder 2 ปีที่แล้ว +2

    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  2 ปีที่แล้ว +1

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

  • @gabdemello
    @gabdemello ปีที่แล้ว

    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  ปีที่แล้ว

      Parse on controller level?

    • @gabdemello
      @gabdemello ปีที่แล้ว

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

  • @reagang8038
    @reagang8038 2 ปีที่แล้ว +1

    Excellent video. Thanks for sharing this

  • @SeCluDred
    @SeCluDred 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

  • @fernandocalmet
    @fernandocalmet 2 ปีที่แล้ว +1

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

  • @perfectionbox
    @perfectionbox 2 ปีที่แล้ว +1

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

  • @margosdesarian
    @margosdesarian 8 หลายเดือนก่อน

    How does this compare with Ardalis SmartEnum approach?

  • @emadali1906
    @emadali1906 2 ปีที่แล้ว +1

    Is this an implementation of the strategy pattern ?

  • @mjunior771
    @mjunior771 10 หลายเดือนก่อน

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  10 หลายเดือนก่อน

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

  • @rafaelscheffer1262
    @rafaelscheffer1262 2 ปีที่แล้ว +1

    great video, thanks for sharing

  • @majormartintibor
    @majormartintibor 2 ปีที่แล้ว +1

    Nice video, thanks for this.

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

    • @majormartintibor
      @majormartintibor 2 ปีที่แล้ว +1

      @@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  2 ปีที่แล้ว

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

  • @porcinetdu6944
    @porcinetdu6944 2 ปีที่แล้ว +1

    That is a massive like

  • @alamir2020
    @alamir2020 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +1

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

    • @MB-Kajtech
      @MB-Kajtech 2 ปีที่แล้ว +1

      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 2 ปีที่แล้ว

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

  • @saeedrezataheri2578
    @saeedrezataheri2578 ปีที่แล้ว +1

    Please Send source code link please!

  • @inayelle
    @inayelle 2 ปีที่แล้ว +1

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

  • @sawek111
    @sawek111 2 ปีที่แล้ว +1

    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  2 ปีที่แล้ว +1

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

    • @sawek111
      @sawek111 2 ปีที่แล้ว

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

  • @amirsolhi3150
    @amirsolhi3150 2 ปีที่แล้ว

    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  2 ปีที่แล้ว

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

  • @mihaikanyaro3460
    @mihaikanyaro3460 2 ปีที่แล้ว +6

    Or use SmartEnum

  • @nanvlad
    @nanvlad 2 ปีที่แล้ว +1

    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

  • @manapotion1594
    @manapotion1594 2 ปีที่แล้ว +8

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

  • @hamidhos8101
    @hamidhos8101 2 ปีที่แล้ว +1

    nice, where is code address?

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

  • @serhiyskaletsky9095
    @serhiyskaletsky9095 2 ปีที่แล้ว +1

    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  2 ปีที่แล้ว

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

    • @errrzarrr
      @errrzarrr 2 ปีที่แล้ว

      Reflection? Please no!

  • @dcp15121980
    @dcp15121980 2 ปีที่แล้ว +1

    How do I unsubscribe if I smash the subscribe button.

  • @HagobSaldadianSmeik
    @HagobSaldadianSmeik 2 ปีที่แล้ว +4

    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  2 ปีที่แล้ว

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

  • @Lammot
    @Lammot 2 ปีที่แล้ว +1

    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  2 ปีที่แล้ว

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

    • @Lammot
      @Lammot 2 ปีที่แล้ว +1

      @@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  2 ปีที่แล้ว

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

    • @Lammot
      @Lammot 2 ปีที่แล้ว

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

  • @nikitafrolov7668
    @nikitafrolov7668 2 ปีที่แล้ว +1

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

    • @necrokora7688
      @necrokora7688 2 ปีที่แล้ว +1

      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  2 ปีที่แล้ว

      @@necrokora7688 💯

    • @benlewies8828
      @benlewies8828 10 หลายเดือนก่อน

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

  • @T___Brown
    @T___Brown 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว +1

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

    • @T___Brown
      @T___Brown 2 ปีที่แล้ว

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

    • @benlewies8828
      @benlewies8828 10 หลายเดือนก่อน

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

    • @T___Brown
      @T___Brown 10 หลายเดือนก่อน

      @@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
    @dakotapearl0 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

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

  • @jorgeutrilla9892
    @jorgeutrilla9892 2 ปีที่แล้ว +1

    Nice!, thanks!.🙌

  • @fenkusingh
    @fenkusingh 5 หลายเดือนก่อน

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

  • @usmanfarooq_dev
    @usmanfarooq_dev 11 หลายเดือนก่อน

    Too much gymnastics...

  • @chrix_app
    @chrix_app 2 ปีที่แล้ว +8

    Or use Ardalis.SmartEnum

    • @allannielsen4752
      @allannielsen4752 2 ปีที่แล้ว +1

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

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 ปีที่แล้ว

      Indeed, great option