
For many years I have been a fan of the “Effective XXX” series, launched by Scott Meyers in 1997 with his
“Effective C ++” . The books in this series contain dozens of tips about your favorite programming language, telling you what to do and what not to do. Such books are easy to read, and they are an excellent source for reflection.
And although these books are a paradise for the reader, they are incredibly difficult to write. To understand this, it is enough to try to write an article from the series “Use / do not use this opportunity in C #” or just remember some holi-chopper in your team that began with an innocent phrase, “let's use as everywhere instead of the type conversion operator Or another similar phrase.
The problem of any advice in the series is to use / do not use / avoid with the abundance of exceptions that follow any such rule. For example, is it worth using mutable value types? Any programmer who came to .NET from C ++ will respond positively (because it is faster!), Then
he will read about the
problems and his opinion will certainly change to the opposite. After that, a barrier may arise in his head, which he can no longer overcome even when he needs to sacrifice security for the sake of efficiency and use structures in his code.
')
All this can lead to the “anchoring” of thinking and
cargo-cult , as a result of which a whole team can avoid certain features of a programming language, just because someone there said it was bad.
That is why when reading (or listening to) any advice from the series, to do something or not, you need to try to understand the reason for this advice. This will summarize this advice and use it in a wider context, and, just as importantly, it will give you an understanding of when to follow this advice, and when it's time to break it!
It is easy to guess that I would not write about all this, if I had no questions for “Effective C #”. To my regret, these questions turned out to be significantly more than I expected.
"Minor" inaccuracies
One of the key features of the books and articles of
John Skit is its rigor in the use of terms and concepts, as well as accuracy in the description of language constructs. John may sacrifice depth, but there will be at least a footnote stating that there are a number of boundary conditions. The author of “Effective C #” in this regard is not so strict and consistent, as a result of which there are blunders of various sizes.
Inaccuracy # 1. Redefinition of static methods"You never override the static Object.Reference and static Object.Equals () because they provide the correct tests, regardless of the runtime type."
The author writes several times that it is not necessary to override (override or redefine) static methods, since they behave as expected. The only problem is that we cannot do this in C # anyway.
Any article or book is a kind of "abstraction", which focuses on key characteristics, while omitting unnecessary details. The difficulty with this is that this "abstraction" does not address important aspects.
Inaccuracy # 2. The lifetime of local variablesAll reference types, even local variables, are allocated on the heap. Reference type becomes the garbage.
The garbage collector is more complicated than it may seem and local variables can be reachable for the collector before the method is completed!
Inaccuracy # 3 . About operator ==“No matter what type is involved, a == a is always true.”
In fact, for the .NET platform, this rule is not always executed. Can you give an example when this condition is violated?
Inaccuracy # 4. Interface virtuality“Not at all, not by default.
...
Interface methods are not virtual. When you implement an interface, you’ll
I agree that interface inheritance has its own characteristics. Yes, any implementation of the interface method will implicitly be “sealed” (sealed), but this is a characteristic of the method that implements the interface method, and not the interface itself.
Inaccuracy # 5. The procedure for creating objects"Here is the order of operations for the first instance of a type:
- Static variable storage is set to 0.
- Static variable initializers execute.
- Static constructors for the base class execute.
- The static constructor executes.
- ... "
Now we know that the
order of calling static constructors is not so simple . In fact, a call to the static constructor of the heir does not lead to a call to the static constructor of the base class, and when creating an instance of the heir, the static constructor of even the type being created may not be called.
But if the previous inaccuracies can be attributed to my pedigree, then there are a number of more serious moments.
Inaccuracy # 6. About collection iteratorsIn Council 21, the author provides a wonderful example of using private inner classes to implement collection iterators. As a confirmation of this approach, the following citation is given:
“The <.NET Framework </ i> </ b> </ b> </ b> </ b> </ b> </ b> </ b> </ b> </ gt; Being private gives many many ... "
Here are two problems: firstly, enumerators of collections are structures, and secondly, these structures are open. Iterators are a well-known source of incomprehensible behavior, since iterators are inherently variable, and variable structures are very dangerous.
I would understand if this example was hypothetical (after all, this example is not so bad for its own collections). But in a book called “Effective C #,” such liberties and mistakes seem to me very strange.
Inaccuracy # 7. About Equals and GetHashCodeThe Equals and GetHashCode methods have a lot of questions (
Inaccuracy # 3 is also from this area, by the way, my answer to this question is: Double.NaN).
With all these Equals and GetHashCode, you can really break a leg, but, first, the author has devoted more than one section to this topic, and second, we read advanced books to find answers to such questions.
So, for example, the author writes about the need to override the GetHashCode method:
“For reference types, it works but is not efficient. For value types, the base class version is
often incorrect . ”
The inefficiency of the Object.GetHashCode method from the point of view of the author is explained as follows. As a GetHashCode value for objects, an internal counter is used, which increases as each object is created. Since the value of the counter is not random, the resulting hash code will lead to frequent collisions and low search efficiency.
Simple experiments show that the CLR behaves somewhat differently (calling (new object ()). GetHashCode () twice results in completely different values. And
fast googling proves that the implementation is somewhat more complicated.
The second part of the statement is even stranger. In fact, even the implementation of the GetHashCode method returning 42 is correct. Yes, ineffective, but absolutely correct. This time, the author insists that the implementation of the ValueType.GetHashCode method simply returns the hash code of the first field. This is almost the case, and the default implementation can return the transformed hash code of the first field. Yes, this implementation can lead to a large number of collisions, but it can not be called incorrect.
And in general, if we get into such details of the implementation, it is better to paint them in more detail, explaining not only the current behavior, but also the reasons for such a realization.
Is it really that bad?
Whether the book will be useful is highly dependent on your experience and, most importantly, the attitude to the readable material. If there is a lot of experience, then you simply will not find anything new. If you are concerned with someone else's advice with healthy pragmatism and have not read
Skit or
de Smet , then this book can be a good source for discussing the various possibilities of C #.
NOTEIf everything is so ambiguous, then what does this book make a
list of classic books on C # /. NET ? I honestly admit that my opinion was based on the first edition of this book, read about 4-5 years ago. Would I add it if I made this list today? Not sure! Nevertheless, this is a rather unique book of its kind, albeit with imperfect execution. Therefore, until the appearance of a real replacement in the market, I will not delete it from this list.
Rating: 3