📜 ⬆️ ⬇️

There is nothing wrong with types in C #

After reading the article “ Strengthening type control: where in the typical C # project there is an unsolicited element of weak typing? I was rather surprised by the fact that this approach was erroneous and that no one paid attention to it.

The author of the post cites the idea that a method that returns a reference type, an object of which is created from a certain repository, must, one way or another, ensure that the returned object will not be null. At the same time, in one of the examples he uses contracts, which contradicts their principles. I want to make out the fallacy of this approach.

We have a method GetItem , which gets an object from a certain repository and must, according to the author's intention, ensure that the object will not be null.

 public Item GetItem(int itemId) { return dataAccessor.getItemById(itemId); } 

')
If the required object was not found in the repository, dataAccessor will return null, which will be given to the client, and without checking the value for null, it will receive a NullReferenceException. So how can you guarantee that this will not happen?

You can squeeze a null check inside the method and if the object is not found, throw Exception. But with this we simply rename a NullReferenceException to, for example, ItemNotFoundException. Moreover, the client is now obliged to add a try-catch block each time this method is accessed. Customer writer will be very happy.

Another option is to add a post-condition to the method. Like this:

 public Item GetItem(int itemId) { Contract.Ensures(Contract.Result<Item>() != null); return dataAccessor.GetItemById(itemId); } 


It all becomes very bad and the kittens in the world becomes smaller. After reading the contract, any person should understand that this method will always return an object, with any identifier provided. But this is impossible. The method cannot guarantee that the object with this Id is in the repository. So you need to add a pre-condition that will help the poor thing and push all the problems to the client:

 public Item GetItem(int itemId) { Contract.Requires(dataAccessor.GetItemById(itemId) != null) Contract.Ensures(Contract.Result<Item>() != null); return dataAccessor.GetItemById(itemId); } 


Fun, isn't it? To get an object from the repository, you must first pull the object out of the repository, for which you need to pull the object out of the repository, for which ...

The truth is that there is no spoon, no one can guarantee the existence of an object in the repository. The only way to ensure the existence of any object is to create it and pinch its link. Everything else is a bad design. Data access layer should get the object from the repository and transfer to the client. He does not know how to handle the absence of an object and is not at all aware of whether this is bad. The logic level is responsible for this.

The inability to guarantee the existence of an object out of sight, leads to the second idea given in the article by reference above. Use a nullable container for reference types. I rephrase: nullable reference. Oil oily. It was strange to learn about the popularity of this pattern.

The reference type can refer to null. Why insert one reference type into another and add the HasValue property to the container is absolutely not clear to me. To check on HasValue? What prevents access to the contents of the object without this check? You can just as carelessly do not check for null through inequality. Moreover, this pattern is not only useless, but also harmful. Take a look at the following code:

 public Maybe<Item> SomeMethod() { var mbItem = GetMbItem(); if(mbItem.HasValue) { var item = mbItem.Value; ModifyItem(ref item); } return mbItem; } 


If the ModifyItem method has changed the object, the SomeMethod method will return the container with the old object. Go then, look for this bug.

In general, I consider the practice shown in that article harmful. To check the reference to null, in C # there are standard tools, but in order to avoid NullReferenceException, you need to be careful. Complicate the system for this is not necessary. Working with repositories you need to monitor the integrity of the data and not run away from it. If there is no object in the repository that must be there, it is much, much worse.

Source: https://habr.com/ru/post/200884/


All Articles