Avoid Returning Null From Methods - There Is a Better Way To Write Them!

Sdílet
Vložit
  • čas přidán 21. 08. 2023
  • Download the source code: / source-code-for-88052099
    We've all been there: You design a slick C# method, but then it might need to return... null. How many times have you crafted a method, sometimes a complex one, only to encounter the inevitable predicament of possibly returning null?
    The ripples from this single decision can create waves in your code. Suddenly, there's a surge in downstream complexity. Pesky null checks, null propagation, and null coalescing start popping up everywhere and before you know it, you're deep-diving into a sea of operators, attempting to keep your code stable. The once crisp and clean code begins to resemble a tangled web. And readability? Well, it is long gone already!
    But what if we could turn the tables and say that every method you design must always return an object, a meaningful one? This isn't just about avoiding the null trap; it's about transforming the way you think about and design methods in C#.
    In this video, we will dissect a piece of realistic demo code to showcase how you can improve the domain model, making it not just resilient but also expressive. The secret is in removing a nullable return from a method! The consumers will need not wrestle with potential nulls is over. The calling code will suddenly attain simplicity, clarity, and elegance and - why not say? - only the happy path will remain!
    Thank you so much for watching! Please like, comment & share this video as it helps me a ton!! Don't forget to subscribe to my channel for more amazing videos and make sure to hit the bell icon to never miss any updates.🔥❤️
    ✅🔔 Become a patron ► / zoranhorvat
    ✅🔔 Subscribe ► / @zoran-horvat
    ⭐ Learn more from video courses:
    Beginning Object-oriented Programming with C# ► codinghelmet.com/go/beginning...
    ⭐ Collections and Generics in C# ► codinghelmet.com/go/collectio...
    ⭐ Making Your C# Code More Object-oriented ► codinghelmet.com/go/making-yo...
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    ⭐ CONNECT WITH ME 📱👨
    🌐Become a patron ► / zoranhorvat
    🌐Buy me a Coffee ► ko-fi.com/zoranhorvat
    🗳 Pluralsight Courses ► codinghelmet.com/go/pluralsight
    📸 Udemy Courses ► codinghelmet.com/go/udemy
    📸 Join me on Twitter ► / zoranh75
    🌐 Read my Articles ► codinghelmet.com/articles
    📸 Join me on LinkedIn ► / zoran-horvat
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    👨 About Me 👨
    Hi, I’m Zoran, I have more than 20 years of experience as a software developer, architect, team lead, and more. I have been programming in C# since its inception in the early 2000s. Since 2017 I have started publishing professional video courses at Pluralsight and Udemy and by this point, there are over 100 hours of the highest-quality videos you can watch on those platforms. On my CZcams channel, you can find shorter video forms focused on clarifying practical issues in coding, design, and architecture of .NET applications.❤️
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    ⚡️RIGHT NOTICE:
    The Copyright Laws of the United States recognize a “fair use” of copyrighted content. Section 107 of the U.S. Copyright Act states: “Notwithstanding the provisions of sections 106 and 106A, the fair use of a copyrighted work, including such use by reproduction in copies or phono records or by any other means specified by that section, for purposes such as criticism, comment, news reporting, teaching (including multiple copies for classroom use), scholarship, or research, is not an infringement of copyright." This video and our youtube channel, in general, may contain certain copyrighted works that were not specifically authorized to be used by the copyright holder(s), but which we believe in good faith are protected by federal law and the Fair use doctrine for one or more of the reasons noted above.
    ⭐For copyright or any inquiries, please contact us at zh@codinghelmet.com
    #csharp #objectorientedprogramming #dotnet
  • Věda a technologie

Komentáře • 97

  • @zoran-horvat
    @zoran-horvat  Před 9 měsíci

    Download the source code for this video: www.patreon.com/posts/source-code-for-88052099
    Learn more from the video course *Beginning Object-Oriented Programming with C#* ► codinghelmet.com/go/beginning-oop-with-csharp
    Learn more from related videos:
    Use Null Object Pattern in Your Rich Domain Model ► czcams.com/video/Jf1_AmXO6Zc/video.html
    How to Avoid Null Reference Exceptions: Optional Objects in C# ► czcams.com/video/8-2xr_kBRnQ/video.html
    Build Your Own Option Type in C# and Use It Like a Pro ► czcams.com/video/gpOQl2q0PTU/video.html

  • @rustamhajiyev
    @rustamhajiyev Před 9 měsíci +10

    When returning Null we usually do ourselfes disservice, yes it's easier, we just skip a part of domain logic, throw it into a black hole - Null. But sooner or later we have to payoff for that decision when our app crashes in the runtime. So it's alway a good idea to invest into domain modeling and define explicitly what Null means in the context and the Null Object pattern comes to the rescue.
    Thanks for the content, Zoran, as always! 😊

  • @brendonlantz5972
    @brendonlantz5972 Před 9 měsíci +5

    Good video. The concept of null sometimes serves a purpose in low level code with pointers, but If you do not have a need to deal with null explicitly it's better to avoid it. The null object pattern is the way to go when you have multiple implementations of an interface and want one to do nothing.

  • @DanimoDev
    @DanimoDev Před 9 měsíci +10

    Great video! I personally used to use null or default returns often, but over time they just make code more complex and almost guarantee that you will get a null at some point which you have to handle in ALL consumers. I think I ended up using null coelescing operators more as a bandaid here for bad code. Never good when your code has more questions than the person reading it does.
    To move away from nulls I did opt for a result wrapper type after seeing it used a lot in the community, which works well when used appropriately. But lately I prefer to use a concrete "invalid" instance of the type as this felt much cleaner. Pretty much exactly what you're doing here with the NoDiscount object.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +2

      My experiences are similar to yours.

  • @lucaffo99
    @lucaffo99 Před 9 měsíci +5

    Excellent video, the concept is crystal clear. In fact, it's more expressive and you can specify more than a null object in a simple way. In that way you can add discount operations like add discounts together etc.. that will be a pretty cool video after this one, some custom operators with discounts.
    Good job as always! 🚀

  • @andreibicu5592
    @andreibicu5592 Před 9 měsíci +5

    Useful video, as always!
    After seeing this and your video about monoid, I would love to see a comparison between the two, when should one use one over the other with use cases.
    Thank you!

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +8

      Then you will be pleased to hear that I do have a script in the queue on that topic precisely: Comparing options, Null Object and Special Case, with their respective use cases.

  • @7th_CAV_Trooper
    @7th_CAV_Trooper Před 3 měsíci +1

    This is a great demo for the cascading disaster caused by null return. I'm going to share it with my junior devs. That NoDiscount reminds me of the functional concept Zero. I think you could implement it as Identity as well. A discount is a percentage, so a discount of 1 means 1 * price = price. Liked and subbed.
    I'm a little triggered by the mutable list being returned, but I'll pretend I didn't see it.

  • @fabricehategekimana5350
    @fabricehategekimana5350 Před 9 měsíci +2

    Incredible video ! I am also a fan of error as value that really belong to a specific type. Some functional language implement this functionnality by default but the null object pattern seem super powerful !

  • @axelseven7
    @axelseven7 Před 7 měsíci +2

    Usually when people talk about avoiding null, they just replace it with a meaningless replica which just hides the null and covers it with something more "Pretty"; for example Option.
    At the end of the day, whether you prefer using Nullables or Options is just a matter of syntactical preferences.
    But this, this is great advice.
    Instead of simply side-stepping the null-problem, the Null Object Pattern actually addresses the issues caused by Null Refs; so that, as you said, the pressure is on the Producer of the method, not on the consumer.
    I've known about the Null Object Pattern for a while, but I have to say that the best explanation of it that I've seen anywhere, was your video.
    Thank you very much for always putting out some of the greatest Programming content on the platform.

    • @axelseven7
      @axelseven7 Před 5 měsíci

      @@TapetBart How so? I'm genuinely curious.

    • @evancombs5159
      @evancombs5159 Před 5 měsíci +1

      Yeah the Option psuedo-nulls are an improvement, but not by much. What is presented here is an excellent option if you are dealing with interfaces, but it comes up a bit short when you are not dealing with an interface. For those scenarios we really need Union types in C# where you can return TypeA or TypeB would eliminate all need for nulls, psuedo-nulls, or custom exceptions within C#. We can simulate this with Result types or with a library like OneOf, but these require an increase in boilerplate to do properly. Having a built-in ability to define the Union within the method signature (i.e. public TypeA | TypeB DoSomething()) would eliminate all of the messiness of rolling it ourselves.

    • @axelseven7
      @axelseven7 Před 5 měsíci

      @@evancombs5159 I think monads are great in c#, but I think Option is just not that great of a monad, since it's just a wrapper for nullability, which we already have in the form of Nullable / T?. I really wish the .NET team would implement union types in a similar way to the tuple system and make union / intersection type expressions lower in IL to a type that matches it.
      For example :
      T1 | T2 union = new T1();
      Would become
      Either union = new Either( new T1() );
      Kinda like how Nullable works

  • @playonce4186
    @playonce4186 Před 9 měsíci +1

    I like to learn from you, as your a uni teacher that i never had

  • @elijahshadbolt7334
    @elijahshadbolt7334 Před 9 měsíci +1

    The Unity Game Engine overloaded the == equality operator for all their Object types to consider the Null object equal to the C# null value, and it was a very poor design decision, which some of their developers say that if they started from scratch they would not have done it that way. The approach in this video only works well if the class upholds every part of the contract, preconditions, postconditions, class invariants, and side effects that are guaranteed by the interface. The video basically just re implemented whatever concrete type was returned from Enumerable.Empty() , which works because it upholds the contract of IEnumerable which is guaranteed to have zero or more items (i.e. "there may be no discounts" was already implied by IDiscount).

  • @Sydra.
    @Sydra. Před 9 měsíci +2

    First I thought I will use my own generic option/optional/maybe type in situations like this. But then I had the idea to use nullable references as option/optional/maybe type through extension methods (like adding the extension method "ValueOrDefault"). I think it's cheap and fits better to the language. But I didn't have the opportunity to test this in big software.

  • @ahjsbkdjhavkcjhvac
    @ahjsbkdjhavkcjhvac Před 9 měsíci +1

    Excellent lesson

  • @seriyezh
    @seriyezh Před 9 měsíci +1

    Thank you Zoran. This is a very useful technic. I would really appriciate if you could share in some video when to use Null object VS Option. Sometimes it is not so clear (at least to me)

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +1

      You are right, one must decide which form applies best to a problem at hand.

  • @82TheKnocKY
    @82TheKnocKY Před 9 měsíci +3

    I use typescript, not C# and I use the nullish coalescing operator a lot. With GraphQL, the client often fetches different data for the same object. Therefore when I'm making a reusable component, it needs to accept "partial" objects, which may not have these properties. Obviously we don't want that to happen, but the null chaining and ?? operators help me prevent runtime errors.
    In other places, null is simply more accurate. If I'm fetching the prices for a given product, I want the state to be {price: null, priceFetching: true}. State with price 0 would mean something different. This allows me to discern between the price existing/being loaded and it just being 0.

    • @alexanderpoplooukhin7448
      @alexanderpoplooukhin7448 Před 9 měsíci

      Much better to wrap price. Because the type for price could be changed, or you can take a decision to return currency. So, you can use null object pattern for price ;)

  • @Krudor2651
    @Krudor2651 Před 9 měsíci

    It feels better to consume resources that do not return null, but such code also introduce extra memory overhead that may not be suitable for high throughput applications.
    We're substituting checking for a null pointer with dereferencing a newly created heap object that needs to be garbage collected (Depending on what you're returning).
    From what I know, Enumerable.Empty is fairly performant as it'll create a new collection for each type that can be reused during the lifetime of the application, but for any other reference type, instead of checking the pointer, you're creating and pointing to a new heap object that needs to be garbage collected, and it has to dereference that object instead of just checking the pointer, every time.
    I just see the new object being allocated each time and see a possible terrible waste of resources, of course depending on the application.
    I like the idea but probably wouldn't apply this in every scenario.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +3

      I think that this analysis is not the primary concern, especially in large and complex domain models. It is the flexibility and substitutability that we favor more than any local optimization in most cases.

    • @evancombs5159
      @evancombs5159 Před 5 měsíci

      Like you said it depends on the application, and in general the amount of overhead created by this is likely not going to be a concern for the overwhelming majority of applications.

  • @triGataro
    @triGataro Před 9 měsíci +1

    simple and useful 🙂

  • @HikingUtah
    @HikingUtah Před 9 měsíci +1

    This approach makes sense in some cases, such as when you're returning a collection as you are here. However, if you are returning one object, I still think null is simpler, and it can be faster as you don't need to allocate anything to return to handle this case.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +1

      Returning null has one simple drawback - it requires branching logic at the calling end. Make a call in N places, now you need to write that same logic in all N places, on top of having to write N additional unit tests thanks to increasing cyclomatic complexity of each of those calling sites by 1.
      Contrary to that, returning a polymorphic object via a non-null reference, retains the same cyclomatic complexion of all callers, and effectively increases the cyclomatic complexity of the entire code base by one - therefore, you only need to implement branching in one place, and to add one unit test to complete the feature.
      That was practical view. The theoretical view is that one does not burden the caller with what can be finished off at the method resolution level. Zero lines of code, and it still works the same.

    • @HikingUtah
      @HikingUtah Před 9 měsíci +3

      @@zoran-horvat In many instances, branching logic is required anyway if the requested object does not exist. For example, if you're retrieving a database object and it doesn't exist, you'll likely either abort the operation or create the missing object. This is where the simplicity and efficiency of returning null is an advantage.
      I accepted that your point holds more weight for collections, and that other scenarios could be found where it's an advantage. But not in all cases.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      @@HikingUtah You are right. In the remaining cases, there is no viable substitute for a missing object. C# designers have opted to deepen the system of nullable types and avoid introducing optional objects after a bad lesson learned from the Java world.
      On the other hand, we have a bright example of doing optional objects right in Rust.
      In my opinion, that is the way to go. You may refer to my earlier video about optional objects here.

    • @bobweiram6321
      @bobweiram6321 Před 9 měsíci

      @@HikingUtah In the case of a missing database object, if it SHOULD exist, but doesn't, then throwing an exception is ideal. If it MIGHT exist, then returning an optional is ideal. By returning a null, the caller can't determine whether the missing object is expected or unexpected. In your example, caller can't determine whether it should abort the operation, or create an object from a null. Moreover, in either case of optional or exception, additional information can be provided such as the object was deleted or it's busy.

    • @HikingUtah
      @HikingUtah Před 9 měsíci +1

      @@bobweiram6321 First off, I rarely want to just throw an exception when a record is not in the database. This happens all the time and it should be handled correctly. Exceptions are not performant and should only be used for unexpected scenarios. And if I make a call to get a database object, and it returns null. I know the object was not in the database. I also know if it was expected because of context. Really, I'm not sure what you're talking about.

  • @thomasschroter3802
    @thomasschroter3802 Před 9 měsíci +6

    @ 4:40 That is exactly why MS introduced the ? and ?? operators. ?-operator does a great job even on fluent apis. chaining obj?.fn1()?.fn2()?.fnx()?? default is a railway like pattern but requires some discipline by the developer. It sure is great for infrastructure code and should not be exposed to consumer api. Empty types are a clean option, but they can pollute the heap and put load on the GC.

    • @billy65bob
      @billy65bob Před 9 měsíci +3

      For empty objects of this variety, you'd generally use either an immutable Singleton (e.g. DBNull.Value) or a struct to sidestep such GC pressure.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +3

      The core problem with null propagation and null coalescing operators is that they are moving behavior to the calling end. In most cases, in a complex business model, that is just the wrong place for that logic implementation.

  • @davidj5132
    @davidj5132 Před 9 měsíci +2

    I think a better title for this video would be "never return a null collection". Applying this idea to individual objects seems like it would be a nightmare

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +2

      Oh, you can't be further away from the truth. In fact, not only that the object from the demo is not a collection, but this very project has other Null Objects and none of them are collections either.

    • @davidj5132
      @davidj5132 Před 9 měsíci

      So you are suggesting we double the amount of business objects we have in our project? A large enterprise app can easily have over 100 business objects.
      Null isn't some fake abstract comment. It just means a matching object doesn't exist based on the parameters passed in. This solution may work for this exact use case, but to suggest this is a good generic solution is ponderous.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +2

      ​@@davidj5132Why so much anger and so little reasoning?
      Double the amount of business objects? A 1% increase would be closer to the truth.
      Who said null is a fake abstract comment and what does that even mean? I said that null requires implementing domain logic at the calling end, proliferating code duplication.
      I never suggested this is a good generic solution. This is one possible solution to a problem. When it doesn't fit, use a different solution. How hard can that be?

    • @davidj5132
      @davidj5132 Před 9 měsíci

      @@zoran-horvat The title of the video is "Avoid Returning Null From Methods - There Is a Better Way To Write Them" and one of your first statements is "every method must return a proper object". How is that not suggesting this is a generic solution?

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +1

      ​@@davidj5132Because there is another solution which is not this one, and still satisfies the sentence I said in the video - the Maybe monad.
      Trust me, I planned that sentence ahead of time. It is logically sound.

  • @AlexanderRomanenko
    @AlexanderRomanenko Před 9 měsíci +1

    "null reference exceptions are almost impossible to cause nowadays" - Cannot convince my company of this, who have strict policy on using anemic objects, and turn off nullable compile warnings in all projects because it "takes away needed flexibility". If there are any warnings left, they just put exclamation mark there and fix it when users report bugs. T_T

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      Lack of understanding. Compiler is the ultimate tool for static code analysis like no other - its results should be used.

  • @AlexUkrop
    @AlexUkrop Před 9 měsíci +1

    How about using your useful Option which inherits IEnumerable and contains or not only one element with Some or None methods? Very simple and cool idea. Thanks

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +1

      There is a difference between options and Null Object/Special Case. In the latter case, any logic is implemented at the producing end, with the consumer only containing the positive branch.

    • @AlexUkrop
      @AlexUkrop Před 9 měsíci

      @@zoran-horvat agree and using Null (Empty) Objects as the state pattern is very useful. But I like the Option in any case :)

  • @the-niker
    @the-niker Před 9 měsíci +8

    The example given I agree with only because it's internally representing a list of things and it's always preferable to return an empty list than a null. Anything more and it invokes my PTSD from using DBNull.Value before I made extensions to translate to/from null automatically. At any boundary of your code a null object will need to be translated into a null for sanity reasons. Any API enpoint or serialization would be worse off if you have to deal with a null object in the data. It feels like the pattern is just shifting the problems to the peripheries.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +11

      You are right there, but let me remind you that database and I/O, such as serialization, do not map well with the object model. There must always be a sort of mapping for any complex domain model.

    • @roberthyland6554
      @roberthyland6554 Před 9 měsíci

      Very true: DTOs.

    • @brianm.9451
      @brianm.9451 Před 9 měsíci

      I/O returning null isn’t the issue. They are generally producers that should take the initiative to handle bills. If I have a service that calls on a repository class that does I/O I shouldn’t expect my service to know how to handle bills, rather I should operate on known entities which is what DDD demands anyways.

  • @user-uf3pv7uv8v
    @user-uf3pv7uv8v Před 9 měsíci

    I've always liked this way of handling things if given the opportunity in the code base and if it also applies to the language I'm working with.
    *What is your suggestion for intrinsic object types in this regard?*

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      The process outlined in this video is part of object-oriented domain modeling, and hence it applies to domain types, rather than intrinsic types which have no domain meaning. It can hardly have any meaning when applied to intrinsic types and their nullable variants.

    • @user-uf3pv7uv8v
      @user-uf3pv7uv8v Před 9 měsíci +1

      ​@@zoran-horvatOK thanks. Makes sense

  • @ibnfpv
    @ibnfpv Před 4 měsíci

    What are your thoughts on cases when a service need to return an object data or/detaild error? are you in favor on solutions like OneOf or Simple object with IsSuccess like solutions

    • @zoran-horvat
      @zoran-horvat  Před 4 měsíci +1

      I don't use OneOf because it doesn't look anything like what it advertises. For a success/error response, I use the functional Result type (Either). When I need more, I create a discriminated union using records, like I did in a few other videos.

  • @brandonpearman9218
    @brandonpearman9218 Před 9 měsíci

    What if caller needs to behave different? I often see code like if(result == default) which is not so different to if(result == null). probably better to look into returning option.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      Testing whether the implementation is default is possible, but not always the correct course of action. You will find an example in my earlier videos where I find it useful in factory methods that are optimizing composition of objects based on factoring out any defaults that are doing nothing in the final composition. On the other side, testing for default to determine whether to execute logic or not is precisely the wrong mindset - it is through polymorphic calls that we vary behavior, not through branching.

  • @adambickford8720
    @adambickford8720 Před 9 měsíci +1

    I just use `Optional` as every 'clever' abstraction over a lack of a value (null object, etc) all leak and cause issues. You almost always have to check for the 'sentinel' value at some point or its completely hideous to support all the boilerplate to avoid it.
    Optional (and similar containers) let me focus on the logic vs the machinery (loop counters, flags, nulls, threads)

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +1

      There is a difference. Optional objects require logic on the consuming end, where Null Object and Special Case patterns are implemented at the producing end.
      Both approaches have their valid uses in domain modeling.

    • @adambickford8720
      @adambickford8720 Před 9 měsíci

      @@zoran-horvatCompletely understood. I've never had the 'we secretly replaced your null with folgers crystals' not go bad at some point. IME it's just better to explicitly model the lack of a value vs pretend some abstraction can hide it. Its a false economy.

    • @adambickford8720
      @adambickford8720 Před 5 měsíci +2

      @@TapetBart syntax makes a huge difference. In java < 8 we had anonymous inner classes and guava, but a simple pipeline with a couple maps and filters took 3 screens of code to express. The concept was valid, but the syntax was so hideous people rightfully avoided it.

  • @k_pavel
    @k_pavel Před 9 měsíci

    Suppose if method requires valid domain object, and then we pass in some object that is not null, but represents a concept of null object (follows null-pattern).
    Point 1:
    That object doesn't exist in domain model because object of NullClassEntity is not value absence, but entity of concrete class (think of it as, for example, classA and classB both allow value absence, but we should create two NoClassValue for each of them to accommodate pattern requirements).
    Point 2:
    What should method that recived such an object do? Is it special check like if (obj is type) or something? This check will pollute code I think (in comparison to .? or ?? operators).
    As an example:
    We have function that maps one set of objects to another and from this perspective map from objectA to null is valid and totally acceptable even in math.
    So doesn't that pattern look old in modern C#?

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +1

      Null Object is representing one valid case in domain modeling. However, as you have rightfully pointed out, there are the cases where a substitute object does not apply. In that case, optional objects are the modern course of action.
      You can watch these two videos for ideas that extend in that direction:
      czcams.com/video/8-2xr_kBRnQ/video.html
      czcams.com/video/gpOQl2q0PTU/video.html

  • @coderider3022
    @coderider3022 Před 7 měsíci

    Very easy to make everything nullable and end up having to handle it everywhere.

  • @l_t_m_f
    @l_t_m_f Před 9 měsíci

    also can apply for c++

  • @jacobstamm
    @jacobstamm Před 9 měsíci

    What I don't like about this design is that the consumer now has to know about this NoDiscount class in order to check the empty case. In fact, every domain model class which otherwise might not have needed an interface will now need one so that a "No X" class can be made for each one. Instead of just returning, say, Location?, now we need Location : ILocation and NoLocation : ILocation, and the caller has to know about both. I'm not sure it's worth it.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      Why would the consuming class know about the alternate implementation? Does that also stand for other polymorphic implementations? It is a polymorphic call like any other.

    • @jacobstamm
      @jacobstamm Před 9 měsíci

      @@zoran-horvat Maybe I’m misunderstanding. Using your example, if the consuming code needs to check the empty/null case, wouldn’t it need to compare the result against an instance of NoDiscount? The null equivalent would be “if (discount == null)”

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      @@jacobstamm Actually, I never did that in the final code. The resulting code is literally the happy path from the initial implementation and nothing else. By introducing the Null Object, I was able to remove the last branching instruction from the calling code.

    • @jacobstamm
      @jacobstamm Před 9 měsíci

      @@zoran-horvat Yes, but my question is, what do you do when the calling code *does* need to check for null for business logic? Wouldn’t it need to know about NoDiscount and compare against that?

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      @@jacobstamm If that is a regular case with some type, then polymorphism is not the right tool. You can use optional objects in that case, for example.
      You can see how that is done in Rust. There, the language doesn't allow you to return null from a regular function, and still you can write all the code as in any other language that allows null.

  • @edwinmartens509
    @edwinmartens509 Před 7 měsíci

    If you are searching for something, but you didn't find anything, returning NULL as being "nothing" is totally correct ! There was no book with that title for example. And yes you can also not use any property of something you did not find in the first place !... Not convinced ? go to a bookstore, ask for a title they don't have , then ask for the price and look at the face of the bookseller ;-)

    • @zoran-horvat
      @zoran-horvat  Před 7 měsíci

      What are you saying, that when you ask a book seller for the price of a nonexistent book, and look into their face, they will throw a NullReferenceException?
      By the way your example is wrong for the video you just watched. You have explained a use case for an optional object.

    • @edwinmartens509
      @edwinmartens509 Před 7 měsíci

      @@zoran-horvat
      No I am saying that you cannot ask for a price, delivery time or any other property of a book that ( in that context) does not exist !
      Which is the meaning of NULL,... there is no such book ! A NullReferenceException is what would be thrown if, knowing there is no such book , you still ask for the price !
      The poor man cannot give you the price of something he does not have !

    • @zoran-horvat
      @zoran-horvat  Před 7 měsíci

      @@edwinmartens509 Null branching is a technique that plagued programming during 1980s and 1990s leading to numerous bugs and design flaws. It was wrong, and it was abandoned.
      There is no reason to go through the process of rediscovering the principles of programming all over again just because some people forgot the errors of the past.
      You have polymorphism and you have optional objects - choose. But don't dump null back into the game again.

    • @edwinmartens509
      @edwinmartens509 Před 6 měsíci

      @@zoran-horvat
      Null branching ?
      Branching is jumping to a (program counter) address. Your comment makes me wonder if I'm even talking to a computer scientist.....
      Yes the wrong handling of null pointers can lead to bugs, but that has more to do with knowing how to write code than with overall design.
      Where is polymorphism involved when there is no result ? You ask if a zoo has a certain animal. They tell you there is no such animal and then you want to know if that non existing animal is a mammal or a reptile ?
      "that plagued programming during 1980s and 1990s leading to numerous bugs and design flaws. It was wrong, and it was abandoned."
      Yes that is why is is still used everywhere...

    • @zoran-horvat
      @zoran-horvat  Před 6 měsíci

      @@edwinmartens509 I see you don't know Rust. Try to learn it. Then switch over to their channels and comment how they are not computer scientists.

  • @edwardferron
    @edwardferron Před 9 měsíci +1

    Good video, so dramatic

  • @Bankoru
    @Bankoru Před 9 měsíci

    Nowadays I just use Option

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci

      Option and Null Object are supplementary - they both have their use in domain modeling.

  • @josebarria3233
    @josebarria3233 Před 9 měsíci +2

    Im waiting for the FP fanboys to come and say that functional is better 😂

  • @iiwaasnet
    @iiwaasnet Před 9 měsíci

    Adding items to the List can be "chained" by writing an extension method, that takes every item of the resulting enumeration and calls List.Add(). You can still use ?. to stop call execution on Null reference. Extension method can be reused in many other places. NullDiscount type is very specific and brings no value to the domain, just pollutes it.

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +1

      One should be careful with methods that cause a side effect and return a result. One or the other is fine, but both at the same time can cause confusion and bugs. One notable counterexample are fluent builders, where naming and usage scenarios help avoid confusion.

  • @pavel3596
    @pavel3596 Před 8 měsíci

    In my opinion, C# is sorely missing what Kotlin has implemented in its sealed classes, which are de facto algebraic datatypes. One can mimick this approach in C# as well, but the implementation is not error-prone. At least I did not find a way.

  • @MDealer
    @MDealer Před 9 měsíci

    Null reference issues are a legacy from the past. Soon there will be no more reason (such as performance) to avoid making functions tolerant to null references. Just wait a bit for photonic computing.

  • @jrgalyen
    @jrgalyen Před 9 měsíci

    This is stupid. It doesn’t serialize null to your fake null substitute. And it is worse when you get to an integer. 0 is NOT a substitute for null. Neither is int.max or int.min
    Null means the value doesn’t make sense. So short circuit or throw an exception. Stop writing g bad code.
    Also, change ALL warnings to errors in your project file. If you want to do as this video describes, switch to visual basic with its default weak duck typing and ambiguous assumptions
    # end rant

    • @jrgalyen
      @jrgalyen Před 9 měsíci

      to summarize, your alternative violates json schema, openapi, avro, and the plathura of data contract syntax out there
      This is not standard and should be avoided. Especially considering teamwork

    • @zoran-horvat
      @zoran-horvat  Před 9 měsíci +4

      Is it grammatical to say that this is the wrongest answer of all times?