Chapter 2 CLR execution model
Various components of the hosting module: pe32 or pe32 + header, CLR header, metadata, il (intermediate language)Code.
The advanced language usually only discloses a subset of all CLR functions. However, the Il assembly language allows developers to access all CLR functions.
The jitcompiler function is responsible for compiling the Il code of a method to the local CPU command. Because Il is compiled in "instant" (just in time), this component of CLR is usually called jitter or JIT compiler.
Microsoft defines a common language specification (CLS), which defines a minimum set of functions that must be supported by all languages.
Chapter 4 generate, package, deploy, and manage applicationsProgramAnd type
The response file is a text file that contains a set of compiler command line switches.
In addition to explicitly specified files on the command line, the compiler will automatically find two files named CSC. RSP (local and global ).
If a configuration conflict occurs between the local file and the global response file, the setting of the local file prevails. Similarly, the settings explicitly specified on the command line overwrite the settings in the local response file.
To increase interest, select "View"> "Statistics" in ildasm to display interesting information.
Assemblyversion is very important. It uniquely identifies an assembly. An assembly is closely bound to a specific version of the referenced assembly.
Chapter 2 shared assembly and forced named assembly
Obviously, it is not enough to distinguish an Assembly Based on the file name. CLR must provide a unique identification mechanism for the Assembly. This is why the "Strongly-named assembly" came from. A strongly-named assembly has four important attributes, which jointly uniquely identify the Assembly: a file name (excluding the extension), a version number, and a language culture (Culture) ID and a public key.
Because the public key is a very large number, a small hash value derived from the Public Key is often used. This hash value is called
Public Key tag (Public Key token ).
CLR never uses public key tags when making security or trust decisions, because several public keys may get the same public key tag after hash processing.
If an assembly is to be accessed by multiple applications, it must be put in a known directory, and when CLR detects a reference to the Assembly, the directory must be checked automatically. This known location is calledGlobal Assembly Cache(Global Assembly Cache, GAC ).
When you install a strongly-named assembly to GAC, the system performs a check to verify that the file containing the list is not tampered. This check is only executed once during installation. Conversely, when a strongly-named assembly is loaded from a non-GAC directory, CLR verifies the Assembly's inventory file and verifies that the file content has not been tampered, This will cause the file to be loaded every time Additional performance overhead.
Chapter 4 Basic Types
The is and as operators will never throw an exception.
When the is operator is used as follows:
If (O Is Employee)
{
Employee E = (employee) O;
}
CLR checks the type of the two objects, which undoubtedly affects the performance. C # provides the as operator to simplify the code writing and improve its performance.
Employee E = o As Employee;
If (E! = Null ){}
If O is not compatible with the employee type, the as operator returns NULL, which only causes CLR to verify the type of the object once. The IF statement only checks whether e is null, which is much faster than the type of the validation object.
C # The Compiler providesExternal alias(Extern alias) function, which solves this rare but still possible problem: distinguish different assemblies through programming, rather than just distinguish different namespaces. The external alias also allows access to a type from two (or more) different versions of the same assembly. For more information about external aliases, see C # language standards.
In addition to enabling or disabling overflow checks globally, programmers can also control overflow checks in specific areas of the Code. C # by providingCheckedAndUncheckedOperators to achieve this flexibility.
Chapter 2 primitive type, reference type, and Value Type
If you overwrite a defined typeEqualsMethod, you should also rewrite it.GethashcodeMethod To ensure equalityAlgorithmIt is consistent with the object hash code algorithm. This is because in the implementation of the system. Collections. hashtable type, system. Collections. Generic. dictionary type, and other sets, two objects must have the same hash code to be equal.
Do not confuseDynamicAndVaR. UseVaRDeclaring a local variable is just a simplified syntax that requires the compiler to deduce a specific data type based on an expression.VaRKeywords can only be used to declare local variables within a method, whileDynamicKeywords can be used for local variables, fields, and parameters. The expression cannot be convertedVaRBut can be transformedDynamic. Must be explicitly initializedVaRDeclared variables, but do not need to be initializedDynamicDeclared variable.
When the Code uses dynamic expressions/variables to call a member, the compiler generates special il code to describe the required operations. This special code is called payload (payload ).
Although the syntax can be simplified with dynamic functions, it also depends on whether it is worthwhile. After all, loading all these sets and additional memory consumption will have an additional impact on performance.
Chapter 2 types and member Basics
When building an assembly, you can useSystem. runtime. compilerservicesThe name of a namespaceInternalsvisibletoTo indicate other assembly that it considers as "youyuan.
When designing a type, minimize the number of defined virtual methods.First, calling a virtual method is slower than calling a non-virtual method. Secondly, the JIT compiler cannot embed (Inline) virtual methods, which further affects the performance. Third, virtual methods make Version Control of components more vulnerable. Fourth, when defining a base type, you often need to provide a set of simple methods for overloading (convenience method ). If you want these methods to be polymorphism, the best way is to make the most complex method a virtual method, so that all simple methods with heavy loads become non-virtual methods.
Chapter 1 Method
Do not call any virtual methods that will affect the constructed object in the constructor. The reason is that if this virtual method is overwritten in the derived type of the type to be instantiated, the rewrite implementation will be called. However, in the inheritance hierarchy, fields are not fully initialized. Therefore, calling virtual methods will lead to unpredictable behavior.
Performance of the Type constructor : Static fields initialized in inline mode (An added
Beforefieldinit Metadata-marked record items), as long as they are initialized before access, the specific time does not matter. While Explicit Type constructor It may contain code with side effects, so you need to precisely take the running time.
There are potential version control problems with the extension method.If MicrosoftStringbuilderClass addedIndexofThe instance method is the same as the prototype that my code is trying to call. When re-compiling my code, the compiler will bindIndexofInstance method. In this way, my program will have different behaviors. This version control problem is another cause of caution when using the extension method.
AboutDivision MethodThere are some additional rules and principles to remember:
- They can only be declared in the branch class or structure.
- The return type of the partial method is alwaysVoid, None of the parameters can be usedOutModifier. The division method can includeRefParameter, which can be a generic method, an instance or a static method, and can be markedUnsafe.
- If custom attributes are applied to both of them, the attributes are merged.
- The division method is always regardedPrivateMethod.
Chapter 1 Parameters
Note: If the method is called from outside the module, changing the default value of the parameter may pose a potential risk. Call site embeds default values in its calls. If the default value of the parameter is changed later, but the code of the call site is not re-compiled, it will pass the old default value when calling your method. Consider using the default value 0/null as the Sentinel value:
// Do not do this:
Private Static String makepath (string filename = " Untitled " ){
Return String. Format ( @" C: \ {02.16.txt " , Filename );
}
//But to do this:
Private StaticString makepath (string filename =Null){
ReturnString. Format (@"C: \ {02.16.txt", Filename ??"Untitled");
}
The null join operator (?) is used here (??)
ForThe variable passed to the method by reference. Its type must be the same as the type declared in the method signature.To ensure type security.
Guiding principles for parameters and return types:Specify the weakest type when declaring the parameter type of the method, preferably the interface rather than the base class.(I think it is a basic type ). For example, if you want to write a method to process a set of data items, it is best to use an interface (for exampleIenumerable <t>Instead of using a strong data type (for exampleList <t>) Or a stronger interface type (suchIcollection <t>OrIlist <t>).
Chapter 2 attributes
Anonymous type
Using the anonymous type function of C #, you can use a very simple syntax to declare an immutable tuple ). Tuples are types that contain a set of attributes. These attributes are usually associated in some way.
VaR O1 = New {Name = " Jeff " , Year = 1964 };
This line of code creates an anonymous type.NewThe type name is specified after the keyword, so the compiler will automatically create a type name for me and will not tell me what the name is.
The compiler is "considerate" when defining anonymous types. If it sees that you areSource codeMultiple anonymous types are defined, and these types have the same structure. Then, it creates only one anonymous type definition, but multiple instances of this type. The so-called "same structure" means that in these anonymous types, each attribute has the same type and name, and the specified order of these attributes is the same. In this way, you can check whether two objects contain equal values and assign a reference to an object to a variable pointing to another object. You can also create an implicit array:
VaR People = New [] {
O1, // O1 see the previous section
New {Name = " Kristin " , Year = 1970 },
New {Name = " Aidan " , Year = 2003 },
New {Name = " Grant " , Year = 2008 }
};
An anonymous instance cannot be exposed to the outside of a method.If you want to pass a tuple, considerSystem. tupleType.
Similar to the anonymous type, onceTupleIt cannot be changed (all attributes are read-only ).
Of course, it is very important that,TupleThe producer (the person who writes it) and consumer (the person who uses it) mustItem# A clear understanding of the content returned by the property. For the anonymous type, the actual name of the attribute is determined based on the source code that defines the anonymous type. ForTupleType. All attributes are called by MicrosoftItem#. We cannot change this person.
Chapter 1 events
If an event member is defined, the type must provide the following capabilities.
- The method can be used to register its attention to this event.
- The method can be used to cancel its attention to this event.
- When this event occurs, the registered method will receive a notification.
AboutDesign the types of events to be publishedThis section is very detailed, includingThread SecurityConsider, bookP228-233It is recommended to read it repeatedly.
Chapter 4 Interfaces
An important feature of interface inheritance is that any instance that can use a named interface can use an instance of the interface type.
At runtime, variables can be transformed from one interface type to another, as long as the object type implements these two interfaces.
String S = " Jeffrey " ;
Icomparable comparable = s;
Ienumerable enumerable = (ienumerable) comparable;
DefineDisplay Interface MethodThe specified accessibility is not allowed (for examplePublicOrPrivate). However, when the compiler generates method metadata, its accessibility is automatically setPrivateTo prevent other codes from calling interface methods directly when using class instances. To call an interface method,Only one variable of the interface type can be used.
Chapter 2 character, string, and Text Processing
System. Security. securestringClass can be used to protect sensitive string data, such as passwords and credit card information.
ConstructSecurestringObject, it will allocateUnmanagedMemory block, which contains an array of characters. The reason for using the unmanaged memory is to avoid the "looweb" of the garbage collector ".
Use oneSecurestringMust be specified for the C # compiler during compilation./UnsafeSwitch option.
Chapter 2 array
If you only need to copy some elements in the array to another array, you can selectSystem. BufferOfBlockcopyMethod, its execution speed ratioArrayOfCopyFast method. However,BufferOfBlockcopyThe method only supports the primitive type, and does not provide the imageArrayOfCopyThe transformation capability in the same way.
To reliably copy elements from one array to another, useSystem. ArrayOfConstrainedcopyMethod. This method ensures that the data in the target array is copied without damaging the data, or an exception is thrown.
Note,Array. CopyMethod execution isShortest copy. In other words, if the array element is of the reference type, the new array will reference the existing object.
If performance is the primary goal, avoid allocating hosted array objects on the stack. Instead, an array should be allocated on the thread stack, which is passed through C #'sStackallocStatement.StackallocStatement can only create one dimension0Base, an array composed of Value Type elements, and the value type cannot contain any referenced fields.
Unsafe {
Const Int32 width = 20 ;
Char * Pc = Stackalloc Char [width]; // Allocate an array on the stack
...
}
Generally, because the array is a reference type, the array fields defined in a structure actually point to a pointer or reference of the array. The array itself is outside the memory of the structure. However, the array can also be directly embedded into the structure like the chararray structure in the code below. In this way, an array is allocated on the stack.
Internal Unsafe Struct Chararray {
// This array is embedded in the structure in an inline manner.
Inline (embedded) array
Public Fixed Char characters [ 20 ];
}
Chapter 4 custom attribute
When an attribute is applied, C # allows a prefix to specify the target element to which the attribute is applied. However, in some cases, the prefix must be specified to clearly indicate our intention to the compiler.
[ Return : Someattr]
Public Int Somemethod ()
{ Return Somevalue ;}
To inform the compiler of the valid application scope of the custom attribute, you must apply it to the attribute class.System. attributeusageattributeClass.
AttributeusageattributeOne of the attributes of isInheritedIt indicates whether attribute is applied to both the derived class and the override method when applied to the base class. Note that. NET Framework only considers that the class, method, attribute, event, field, method return value, parameter, and other target elements can be inherited.
CallAttributeOfGetcustomattributeOrGetcustomattributesMethods, these methods call the constructor of the attribute class internally, and may call the set accesser method of the attribute. In this way, it is equivalent to allowing unknown code to run in the appdomain, so it is a potential security risk.
UseSystem. reflection. customattributedataClass, you can disable the execution of code in the attribute class while searching for the attribute.
Chapter 4 exception and status management
If you only useThrowKeyword itself to throw an exception object, the CLR will not reset the starting point of the stack.
In your code, if you determine that the state has been damaged to a level that cannot be repaired, you should destroy all the corrupt states to prevent it from causing more damage. Then, restart the application, initialize the state to a good state, and hope that the State will not be damaged. In this case, you can callAppdomainOfUnloadMethod to uninstall the entire appdomain.
If you think the status is too bad so that the whole process should be terminated, you should callEnvironmentStaticFailfastMethod.
To add additional data or context to an exception, you canDataAdd data to a property (a set of key/value pairs) and then throw (onlyThrow).
AboutConstraint execution region (CER),Runtimehelpers. prepareconstrainedregionsIs a very special method. JIT CompilerTryIf this method is called before the block,TryAssociatedCatchAndFinallyBlock. The JIT compiler loads any assembly, creates any type object, calls any static constructor, and compiles JIT for any method. If any of the operations causes an exception, the exception will occur in the threadTryBlockBeforeOccurred.
Chapter 4 Automatic Memory Management (garbage collection)
UseSystem. runtime. constrainedexecution. criticalfinalizerobjectType. CLR treats this class and its derived classes in a very special way: 1.FinalizeThe method is compiled in advance. 2.Non-criticalfinalizerobjectDerived typeFinalizeMethod.CriticalfinalizerobjectDerived typeFinalizeMethod.
System. runtime. interopservicesInCriticalhandleClass, except for the reference counter functionSafehandleClass.CriticalhandleClasses and Their Derived classes sacrifice security in exchange for better performance.
All definedFinalizeThe methods must be of the same type as described in this section.DisposeModeSo that users of the type have more control over the resource survival. However, the type can also be implementedDisposeMode, but not definedFinalizeMethod.
If the class defines a field and the field type implements the dispose mode, the class itself should also implement the dispose mode.DisposeMethod should be the object referenced by the dispose field.
We recommend that you only callDisposeOrClose: Determine whether resources must be cleared (for example, to delete an opened file); or are you sure you can safely callDisposeOrCloseAnd you want to delete the object from the final list to prohibit the object from being upgraded (to another generation) to improve performance.
If a class needs to wrap a limited number of local resources, you should useSystem. runtime. interopservicesOfHandlecollectorClass to indicate how many instances the Garbage Collector actually needs to consume resources. Objects of this class monitor this count internally. When the Count increases, garbage collection is enforced.
Internally,GC. addmemorypressureAndHandlecollector. AddMethod callGC. CollectMethod, forced garbage collection to start before the 0th generation reaches the budget.
System. RuntimeThe namespace providesMemoryfailpointClass, which allows you to check whether there is sufficient memory before starting a memory-consuming algorithm. Note that the requested memory has not been physically allocated. This means that the algorithm is more likely to obtain the required memory and run it successfully. The purpose of this class is to help you write more robust applications.
A great tool for monitoring application object allocation:CLR profiler.
Chapter 2 CLR boarding and appdomain
If the thread is executing the class constructor of the type,CatchBlock orFinallyThe code in the block, the code in the CER, or the unmanaged code, the thread is not safe.
Chapter 4 assembly loading and reflection
Four reflection methods:
1. ExploitationTypeOfInvokememberTo bind and call a member.
2. ExploitationFieldinfo, methodinfo, and propertyinfoClasses can be bound at a time and called multiple times. This technology can produce code with better performance.
3. bind to an object or member, and create a delegate to reference the object or member. This technology can generate code faster than the previous technology.
4. Use the dynamic primitive type of C # To simplify the syntax used to access members.
Use runtimetypehandle, runtimefieldhandle, runtimemethodhandle to reduce the memory consumption of processes.
Chapter 4 runtime serialization
AvailableSerializationCreate an objectDeep copyOr a clone.
When an object is serialized, the full name of the type and the name of the definition assembly of the type are written into the stream. By default,BinaryformatterThe complete identifier of the Assembly is output, including the Assembly file name (without an extension), version number, language and culture, and public key information. When deserializing an object, the formatter first obtains the Assembly ID information and usesAssembly. LoadMake sure that the Assembly is loaded into the executing appdomain.
The best way to control the serialization and deserialization processes is to useOnserializing, onserialized, ondeserializing, ondeserialized, nonserializedAndOptionalfieldAnd other attributes.
AvailableIserializationsurrogateInterface to take over the serialization and deserialization of a specific type (very rare ).
AvailableSystem. runtime. serialization. serializationbinderClass to deserialize an object into a different type. Therefore, you must first define your own type so that it can be retrieved from the abstract class.Serializaitonbinder.
Chapter 4 asynchronous operations restricted by computing
If the execution time of the callback method is long, the timer may be triggered again. This may cause multiple thread pool threads to execute your callback method at the same time. To solve this problem, I suggest constructingTimerIsPeriodParameter specifiedTimeout. Infinite. In this way, the timer is triggered only once. Then, in your callback method, callChangeMethod to specify a newDuetimeAndPeriodParameter specifiedTimeout. Infinite.
Chapter 2Synchronization construction of primitive threads
When threads communicate with each other through the shared memoryVolatilewriteTo write the last value, callVolatilereadTo read the first value.
Spin locks should only be used to protect code areas that will execute very quickly.
There is a generic MethodMorphEncapsulatedInterlocked anything Mode. It mainly solves the implementation of atomic methods in the case of multithreading (see the book P720-721 ).
Delegate int morpher <tresult, outcome> (INT startvalue, targument argument, out tresult morphresult); static tresult morph <tresult, targument>, targument> morpher) {tresult morphresult; int currentval = target, startval, desiredval; do {startval = currentval; desiredval = morpher (startval, argument, out morphresult); currentval = interlocked. CO Mpareexchange (ref target, desiredval, startval);} while (startval! = Currentval); Return morphresult ;}
The book has been read and will not be updated.