📜 ⬆️ ⬇️

Carefully, true class contracts may differ from formal

In short, this article will focus on Liskov's inheritance rule, the difference between NotifyCollectionChangedAction.Reset contracts in the .NET Framework 4 and the .NET Framework 4.5 , and which of these two contracts is true and which one is wrong.



According to the Liskov principle , the heir class must inherit the contract of the base class (with the possibility of adding its own specifics that do not contradict the original contract).

I will give an example. Imagine that the Add List method is virtual. If you create an inheritor from List <>, then the Add method in it must add exactly one element to the collection. If an element is added only when a certain condition is fulfilled, or the element and its copy are added, then the custom code, which expects that after calling Add Count, increases by exactly one, will become inoperative. The behavior of inherited classes should be expected for code that uses a variable of the base type.
')
Now let's imagine that you are going to use List <> in your code. Judging by the name Add and parameters (one element), the method should add one element to the collection. You have used the sheet many times, and you are sure that it is so. You can ask a colleague, and he does not hesitate to confirm that this is the way it is. But let's imagine for a moment that you go to msdn, see the documentation, and it says that Add simply “changes the original collection”, i.e. does anything. In this case, we will call the contract, which is characteristic of the base class, and on which everyone rely, true , and the one that is described in the documentation - formal .

If, creating a class heir, you rely on formal behavior, not on true behavior, then formally you will not break anything, but you will actually create a time bomb that one day can create for someone (most likely not for you) Problems.

An example of the discrepancy between the formal and the true contract is NotifyCollectionChangedAction.Reset. Before version 4.5, Reset meant that the contents of the collection had changed a lot . What does “strong” mean? For some, the addition of three elements is a strong change, but for some it is not.

In general, Reset meant "the collection has changed as you like." Since version 4.5, Reset has come to mean cleaning up the collection . Some might think that this change was made in vain, because it violates backward compatibility, but I will say that the guys are great - they noticed in time that the true contract was at odds with the formal one, and promptly corrected their oversight. Using ObservableCollection, you can find Reset only if the object was called Clear (). Programmers who regularly work with ObservableCollection are used to this and consider it the norm. “When can Reset meet?”, You ask them, and they will reply without hesitation: “When Clear was called!”. Naturally, they intuitively believe that this behavior, which has de facto become the standard, should be preserved in the heirs. Therefore, the documentation should state that the Reset is a sign of collection cleanup.

To summarize: in case you implement an heir, rely on the most specific and specific contract among the formal and the true. If you use a class, rely on the least specific contract among the formal and the true.

Using Reset, consider that it can mean anything. When inheriting an ObservableCollection, consider that Reset means clearing the collection.

PS If you are interested in my opinion on the Reset, I think that the developers of the ObservableCollection class should leave the Reset contract in the form in which it is today (sign of collection cleanup), but add to the enumeration an element that signals that the collection has changed as you please, and which would not be used in the original ObservableCollection. The fact is that the only element of the enumeration that signals that several elements of the collection have changed is Reset, the remaining elements of the enumeration signal a change of the single element. Once, in order to achieve acceptable performance, one programmer needed to first change several elements in the collection, and then send exactly one signal to change the collection. And he had no choice but to signal a change in the collection in his successor from ObservableCollection by means of Reset, for lack of other alternatives.

So I think that changing the documentation solved one problem, but at the same time created another (to solve which you need to add another element to the listing). Funny, but sometimes unused reserved items can be useful.

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


All Articles