📜 ⬆️ ⬇️

Interesting notes on C # and CLR



Studying the C # programming language, I came across features of both the language and its execution environment, * some of which, I may say, are “widely known in narrow circles”. Collecting those day after day in my piggy bank, that would ever repeat, which I honestly have never done before, I got the idea to share them.

These notes will not make your code more beautiful, faster and more reliable, for this there is Steve McConnell. But they will definitely contribute to your way of thinking and understanding what is happening.

Some of the following will seem too simple, the other, on the contrary, difficult and unnecessary, but where without it.
')
So, we begin:

1) The location of objects and instances in dynamic memory


Objects contain static fields and all methods. Instances contain only non-static fields. This means that the methods are not duplicated in each instance, and the Flyweight pattern is used here.

2) Passing parameters to methods


The structure passes its copy to the method, the Class passes a copy of its reference. But when we use the keyword REF, the structure passes a pointer to itself, and the class passes its original link.
 REF   . 1)   ,     . static void Main( string[] args ) { StringBuilder sb = new StringBuilder(); sb.Append("Hello "); AppendHello(sb); Console.WriteLine(sb.ToString()); } private static void AppendHello(StringBuilder sb) { sb.Append(" World!"); sb = null; } 2)   System.NullReferenceException          null. static void Main( string[] args ) { StringBuilder sb = new StringBuilder(); sb.Append("Hello "); AppendHello(ref sb); Console.WriteLine(sb.ToString()); } private static void AppendHello(ref StringBuilder sb) { sb.Append(" World!"); sb = null; } 


3) Prepare code before execution


The CLR has a CER block that says JIT - “prepare the code before execution, so that when it becomes necessary, everything will be at hand.” To do this, we connect the namespaces System.Runtime.CompilerServices and RuntimeHelpers.PrepareConstrainedRegions.

4) Regular expressions


A regex can be created with the Compiled option — this is the generation of an expression in the IL code. It is much faster than usual, but the first launch will be slow.

5) Arrays


One-dimensional arrays in IL are represented by a vector, they are faster than multidimensional ones. Arrays of one-dimensional arrays use vectors.

6) Collections


It is better to inherit custom collections from ICollection, the IEnumerable implementation is free. But there is no index (very individual).

7) Extending methods


If the name of the extension method conflicts with the method name of the type, then the full name of the extension method can be used, and the type must be passed as an argument.

 StaticClass.ExtesionMethod( type ); 

8) LINQ


LINQ lazy loading (“lazy” loading) - select, where, take, skip etc.
LINQ eager loading (queries are executed immediately) - count, average, min, max, ToList etc. (But if the collection is infinite, then the request will never end.)

9) Sync block


Structural types and primitive (byte, int, long ...) do not have a synchronization block, which is present for objects in the managed heap along with the link. Therefore, the Monitor. () Or Lock () construction will not work.

10) Interfaces


If in C #, the name of the interface in which this method is defined (IDisposable.Dispose) is specified before the method name, then you create an explicit implementation of the interface method (Explicit Interface Method Implementation, EIMI). When explicitly implementing an interface method in C #, you cannot specify an access level (open or closed). However, when the compiler creates metadata for a method, it assigns it a private access level (private), which prohibits any code from using an instance of the class by simply calling the interface method. The only way to call the interface method is to access it via a variable of this interface type.

You cannot do without EIMI (for example, when implementing two interface methods with the same names and signatures).

11) Not in C #, but IL supported


Static fields in interfaces, methods that differ only in the return value, and more.

12) Serialization


When serializing an object graph, some types may turn out to be serializable, and some may not. For performance reasons, the formatting module does not check the possibility of this operation for all objects before serialization. This means that a situation may arise when some objects will be serialized into the stream before the occurrence of the SerializationException exception. As a result, corrupted data appears in the I / O stream. This can be avoided, for example, by serializing objects first in the MemoryStream.

In C #, within the types marked with the [Serializable] attribute, it is not necessary to define automatically implemented properties. The fact is that the field names generated by the compiler may change after each subsequent compilation, which will make it impossible to deserialize type instances.

13) Constants


Constants are placed in the assembly metadata, so if there have been changes, you need to recompile all the assemblies that use it. Since DLL with a constant may not even load.
It is better to use static readonly specifying values ​​in the constructor, it is constantly loaded in assemblies using it, and returns the current value.

14) Delegates


GetInvocationList - returns a chain of delegates, you can call any, catch exceptions, get all returned values, and not just the last.

15) String Comparison


In Microsoft Windows, uppercase string comparison is optimized. * StringComparison.OrdinalIgnoreCase, in fact, translates Char to upper case. ToUpperInvariant. Use string.compare (). Windows uses UTF-16 encoding by default.

16) Optimization for multiple rows


If in an application strings are often compared using a case-insensitive comparison method, or if a set of identical string objects is expected to appear in an application, then CLR-supported string interning should be used to improve performance.

During initialization, the CLR creates an internal hash table, in which the keys are strings, and the values ​​are references to string objects in the managed heap.

17) Safe lines


When creating a SecureString object, its code allocates a block of unmanaged memory that contains an array of characters. The garbage collector knows nothing about this unmanaged memory. You need to clear this memory manually.

18) Security


Managed assemblies always use DEP and ASLR.

19) Designing methods


When declaring the type of method parameters, one should, if possible, specify “minimal” types, preferring interfaces to base classes. For example, when writing a method that works with a set of elements, it is best to declare a method parameter using the IEnumerable interface.

 public void ManipulateItems<T>(IEnumerable<T> collection) { ... } 

At the same time, declaring the type of the object returned by the method, it is advisable to choose the strongest of the available options (trying not to be limited to a specific type). For example, it is better to declare a method that returns a FileStream object, rather than a Stream.

20) Once again about car ownership


It is better not to use automatically implemented AIP properties (author's opinion, guess what).
a) The default value can be set only in the constructor. (Changed to Roslyn C # 6);
b) The problem with serialization (clause 12);
c) You can not put a breakpoint.

21) Configuration File


a) An external XML configuration file can be mapped to any .NET binary code. This file is located in the same directory and has the same name with the word .CONFIG added at the end;
b) If you provide a solution in binary form only, documenting comments can be compiled into an XML file when compiled, so in this situation you can provide users with an excellent set of hints. To do this, you only need to place the resulting XML file in the same directory as the binary file, and Visual Studio .NET will automatically display comments in IntelliSense hints.

22) Exceptions


The CLR clears the starting point of the exception:

 try {} catch (Exception e) { throw e; } 

The CLR does not change the starting point of the exception:

 try {} catch (Exception e) { throw; } 

You can create the FirstChanceException event of the AppDomain class and get exception information before the CLR starts looking for their handlers.

Exceptions are slow; transition to kernel mode.

23) IS and AS


IS - In this code, the CLR checks the object twice:

 if ( obj is Person ) { Person p = (Person) obj; } 

AS - In this case, the CLR only checks the compatibility of obj with the Person type once:

 Person p1 = obj as Person; if ( p1 != null ) { ... } 

24) Check whether there is enough memory before executing


Creating an instance of the MemoryFailPoint class checks whether there is enough memory before the start of an action or throws an exception. However, keep in mind that physically the memory has not yet been allocated, and this class cannot guarantee that the algorithm will receive the necessary memory. But using it will definitely help make the application more reliable.

25) Something about Null


To use a null compatible Int32 you can write:

 Nullable<Int32> x = null;  Int32? x = null; 

The union operator is null compatible values ​​- ?? (if the left operand is null, the operator goes to the following), consider two equivalent expressions:
one)
 string temp = GetFileName(); string fileName = ( temp != null ) ? temp : "Untitled"; 

2)
 string fileName = GetFileName() ?? "Untitled"; 


26) Timers


The FCL library contains various timers:
1) Timer in System.Threading - suitable for performing background tasks in a pool thread;
2) Timer in System.Windows.Forms - the timer is associated with the calling thread, this prevents parallel invocation;
3) DispatcherTimer in System.Windows.Threading. - equivalent to the second, but for Silverlight and WPF applications;
4) Timer in System.Timers. - essentially a shell of the first, Jeffrey Richter advises against using it.

27) Type and typeof


To get an instance of Type for types, instead of the Type.GetType method, typeof is used. If the type is known at compile time, the typeof operation immediately searches for methods instead of at runtime.

28) Using chip


To reduce the amount of code and make it clearer? You can use a using directive like this:
using DateTimeList = System.Collections.Generic.List <System.DateTime>;

29) Preprocessor Directives


a) #IF DEBUG and #ENDIF - used to specify code blocks that will be compiled only in DEBUG mode.
b) #DEFINE XXX; #IF (DEBUG && XXX) - you can add the assembly number “XXX” to the condition.
c)! DEBUG == RELEASE (just in case).
d) #LINE 111 - in the error window will show the line 111.
d) #LINE HIDDEN - hides the line from the debugger.
e) #WARNING XXX; #ERROR YYY - mean XXX - warning, YYY - error.

30) Any * anthony


Covariance - transformation in direct order, the keyword OUT.

 string[] strings = new string[3]; object[] objects = strings; interface IMyEnumerator<out T> { T GetItem( int index ); } T --> R IOperation<T> --> IOperation<R> 

Contravariance is a conversion in the reverse order, the keyword IN.

 interface IMyCollection<in T> { void AddItem( T item ); } R --> T IOperation<T> --> IOperation<R> 

Invariance - implicit type conversion is not allowed.

By default, generic types are invariant. Still generalized classes are called open, in runtime they are closed by specific types of “int”, “string”, for example. And these are different types, the static fields in them will also be different.

31) Extension methods


 public static class StringBuilderExtensions { public static Int32 IndexOf ( this StringBuilder sb, Char char) { ... } } 

Allows you to define a static method that is invoked using the syntax of an instance method. For example, you can define your own IndexOf method for StringBuilder. First, the compiler will check the StringBuilder class or all its base classes for the presence of the IndexOf method with the necessary parameters, if it does not find one, it will look for any static class with a specific IndexOf method whose first parameter corresponds to the type of expression used in the method call.

And this is the implementation of the Visitor pattern in .Net.

32) Execution Contexts


Each thread has a specific execution context. It includes security parameters, host parameters, and logical call context data. By default, the CLR automatically copies it, from the very first thread to all auxiliary ones. This ensures the same security settings, but at the expense of performance. To control this process, use the ExecutionContext class.

33) Volatile


JIT - the compiler guarantees that the access to the fields marked with the given keyword will occur in the mode of volatile reading or writing. It prohibits the C # compiler and the JIT compiler from caching the contents of the field in the processor registers, which guarantees that with all read and write operations, manipulations will be performed directly with the memory.

34) Collection Classes for Parallel Stream Processing


ConcurrentQueue - processing elements by the FIFO algorithm;
ConcurrentStack - processing elements using the LIFO algorithm;
ConcurrentBag - unsorted set of elements, allowing duplication;
ConcurrentDictionary <TKey, TValue> is an unsorted set of key-value pairs.

35) Streams


User Mode Constructs:
a) Volatile structures - an atomic read or write operation.
VolatileWrite, VolatileRead, MemoryBarrier.
b) Interlocking constructions - an atomic read or write operation.
System.Threading.Interlocked (interlocking) and System.Threading.SpinLock (locking with looping).
Both constructions require the transfer of a reference (address in memory) to a variable (recalling structures).

Kernel Mode Constructions:
About 80 times slower than user-mode constructs, but they have a number of advantages described in MSDN (a lot of text).

Class hierarchy:

 WaitHandle EventWaitHandle AutoResetEvent ManualResetEvent Semaphore Mutex 


36) Class Fields


Do not explicitly initialize the fields; do this in the default constructor, and use it with the this () keyword to use constructors that take arguments. This will allow the compiler to generate less IL code, since Initialization occurs 1 time in any of the constructors (and not in all at once the same values, copies).

37) Run only one copy of the program


 public static void Main () { bool IsExist; using ( new Semaphore ( 0, 1, "MyAppUniqueString", out IsExist ) ) { if ( IsExist ) { /*    ,      . */ } else { /*         ,   ,      Main,      . */ } }} 


38) Garbage Collection


The CLR has two modes:
1) Workstation - the collector assumes that other applications do not use processor resources. Modes - with parallel assembly and without it.
2) Server - collector assumes that no third-party applications are running on the machine, all CPU resources are on the assembly! Managed heap is disassembled into several sections - one per processor (with all the consequences, that is, one thread per heap).

39) Finalizer


It performs the function of the last desire of the object before deletion; it cannot last longer than 40 seconds, it is not worth playing with it. Translates an object into at least 1 generation, since not immediately removed.

40) Monitoring and control of the garbage collector at the facility


Call the static method Alloc of the GCHandle object, pass in a reference to the object and type GCHandleType in which:
1) Weak monitoring, we find out that the object is no longer available, the finalizer could be executed.
2) WeakTrackResurrection - monitoring, we find out that the object is no longer available, the finalizer was exactly executed (if available).
3) Normal - control, forcing the object to remain in memory, the memory occupied by this object can be compressed.
4) Pinned - control, forcing the object to remain in memory, the memory occupied by this object cannot be compressed (that is, moved).

41) CLR


The CLR is essentially a processor for MSIL commands. While traditional processors use registers and stacks to execute all commands, the CLR uses only the stack.

42) Recursion


If you have ever seen an application that pauses another for a second and then disappears completely without any error message, it was almost certainly caused by endless recursion. A stack overflow, as is known, cannot be intercepted and processed. Why? We read in books or blogs.

43) Windbg and SOS (Son of Strike)


How many domains are present in the process at once?
- 3. System Domain, Shared Domain and Domain 1 (domain with the code of the current application).

How many heaps (generations) really?
- 0, 1, 2 and Large Object Heap.
Large Object Heap - for very large objects, not compressed by default, only through the configuration in the XML configuration file.

Another difference in the client and server garbage collection (in books is not all so detailed, perhaps inaccurate translation).
- Each HEAP has its own HEAP, each with its own 0, 1, 2 generations and a Large Object Heap.

Create an array larger than 2 GB on 64-bit platforms.
- gcAllowVeryLargeObjects enabled = "true | false"

What to do when there is free memory, but it is impossible to allocate a large continuous area for a new object?
- enable compact mode for Large Object Heap. GCSettings.LargeObjectHeapCompactionMode;
It is not recommended to use, it is very expensive to move large objects in memory.

How fast can I find flow loops (dead-locks) in runtime?
-! Dlk

Sources (not advertising):
1) Jeffrey Richter, “CLR via C #” 3rd / 4th edition.
2) Trey Nash, "C # 2010 Accelerated Professional Course."
3) John Robbins, Debugging Applications for Microsoft .NET and Microsoft Windows.
4) Alexander Shevchuk (MCTS, MCPD, MCT) and Oleg Kulygin (MCTS, MCPD, MCT) ITVDN resource (https://www.youtube.com/user/CBSmatmatTVTV/videos?shelf_id=4&view=0&sort=dd).
5) Sergey Pugachev. Microsoft Engineer (https://www.youtube.com/watch?v=XN8V9GURs6o)

Hopefully, this list came to taste for both beginners and experienced C # programmers.

Thanks for attention!

* Updated, corrected errors, added some moments with examples.
If you find an error, please report it in a personal message.

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


All Articles