What Great .NET Developers Ought To Know (Part Two Mid-Level .NET Developer)
Continue on Scott Hanselman's - What Great .NET Developers Ought To Know
Part Two Mid-Level .NET Developer
Object-oriented programming (OOP): a programming methodology built around data abstraction (i.e. type, class and object) and message communication between objects. The basic concepts of OOP are encapsulation, inheritance, and polymorphism.
Interface-oriented programming (IOP) is an extension of OOP in which all program interdependencies are expressed via abstract interfaces. Abstract interfaces and implementations are strictly separated. As a result, inheritance in IOP is interface-oriented, supports both specialization and adaptation inheritance, and is decoupled from the implementation binding mechanism. (from Generative Programming, Interface-Oriented Programming and Source Transformation Systems)
Aspect-oriented programming (AOP) Eclipse Foundation defines AOP as:
‘A type or style of programming that explicitly takes into account crosscutting concerns, just as object-oriented programming explicitly takes into account classes and objects.’
The essence of AOP model is to identify concerns such as logging, exception handling, etc. that cross-cut several layers in a system, and modularise them into orthogonal units called aspects and inject these aspects at selected execution points in a system dynamically at runtime.
Pointers: Aspect Orienting .NET Components
An interface defines a contract without implementation. A class can implement one or more interface. A class can only inherit one parent class. A interface can inherit many parent interfaces. There is no ‘implementation’ concept in interface. See this code snippet.
namespace ClassLibrary1{
public interface IFace1{
void Foo1();
void Bar1();
}
public interface IFace3{
void Foo3();
}
public interface IFace2 : IFace1, IFace3{
void Foo2();
}
public class Class1 : IFace2{
public Class1(){}
// IFace2 Members
public void Foo2(){}
// IFace3 Members
public void Foo3(){}
// IFace1 Members
public void Foo1(){}
public void Bar1(){}
}
}
When building an assembly or a module, the .NET compiler creates metadata of the assembly at the same time. Metadata contains a type definition table, a field definition table, a module definition table etc. The process to query these metadata tables at runtime is called Reflection. The FCL’s System.Reflection namespace contains types a developer can use to write code to reflect metadata and obtain information about a type/class in the assembly, such like fields, methods, properties and events etc.
Xml Web Services uses standard protocols such like HTTP, XML and SOAP in messaging. It addresses interoperability between disparate, heterogeneous applications.
The .NET Remoting system enables messaging between remote object across AppDomain, process or machine boundaries using communication channels like TCP/IP or HTTP. It doesn’t address interoperability between heterogeneous applications, meaning client and service object must expose/understand the communication interface – object reference.
Message sent over communication channel is called remote object. Remote object is encoded/decoded using native .NET serialization formatters, such like binary or XML encoding like SOAP. Binary encoding is more efficient. XML encoding is preferred choice when interoperability is required.
Some brief Q&As about Remoting.
Isomorphic: In mathematics, an isomorphism (in Greek isos = equal and morphe = shape) is a kind of interesting mapping between objects. (Reference) Two isomorphic sets (such as species) have a one-to-one correspondence between them. For each member of one set, there is a corresponding member of the other set. For example, there is a one-to-one correspondence between the set of lower case letters and the set of upper case letters, with a corresponding to A, etc. (Reference)
.NET classes can be serialised into a XML Schema by using types in System.Xml.Serialization, such like XmlSerializer. (Some code example can be found here)
CLS, Common Language Specification
Early-binding: static binding, validated by compiler. Late-binding: dynamic, type cast, validate by runtime.
public class Point{
public Point(){
}
public int X = 0;
public int Y = 0;
}
public class Line : Point{
public Line(){
}
}
public class Widget{
public Widget(){}
}
public class Foo{
Line line1 = null;
Widget widget = null;
Object object1 = null;
public void Demo() {
//... do something with line1
//early binding, complier will complain
Point p1 = (Point)line1;
Console.Write("Location (X,Y) ({0}, {1})", p1.X, p1.Y);
//compiler complians on the cast
//Point p2 = (Point)widget;
//late binding on a generic type
Point p3 = (Point)object1;
//following line only throws exception at runtime
Console.Write("Location (X,Y) ({0}, {1})", p3.X, p3.Y);
}
}
Assembly.Load also accepts a simple assembly name, which must be an object of AssemblyName, for example:
AssemblyName myAssemblyName = new AssemblyName();
myAssemblyName.Name = "MyAssembly";
myAssemblyName.Version = new Version("1.0.0.2001");
- A simple file name (unencrypted name);
- A version number;
- A cryptographic key pair;
- And a supported culture.
It allows versioning and singing as opposed to a simple filename.
Assembly SampleAssembly = Assembly.LoadFrom("c:\\Sample.Assembly.dll");
Assembly SampleAssembly2 = Assembly.LoadFrom("c:\\Sample.Assembly2.dll");
LoadFile does not load files into the LoadFrom context, and does not resolve dependencies using the load path, as the LoadFrom method does. LoadFile is useful in this limited scenario because LoadFrom cannot be used to load assemblies that have the same identities but different paths; it will load only the first such assembly.
Strongly-named assemblies are signed using a private\public key pair which helps with code verification.
signed assemblies could be placed in thee GAC.
No. It is a Structure an object of it is place in the stack.
Reference Compiling MSIL to Native Code
Just-In-Time compilation. (In .Net context) complier produces MSIL (the portable executable); at the first time the assembly is called, it is compiled into CPU specific native code by the .NET JIT compiler. The benefit: portable executable – ‘write once, run everywhere’; JIT compiles the code and stores result native code in memory at initial call and subsequent calls is accessing in-memory native code hence it is relatively efficient.
NGEN Pre-compilation, or pre-JITing, is the creation of native code from MSIL on the client machine, but instead of being generated at run time it is done as a separate isolated step. NGen is the term used generically for pre-JIT technology in the common language runtime (CLR), and specifically for the command-line utility used to create and store the native code. The native code produced by NGen is stored in true Win32® PE files which are called "native images" or "NGen images" which are in turn stored in the "native image cache" or "NGen cache."
When creating native images, NGen simply loads the JIT compiler and invokes it to create native code for the assembly, which is then persisted into the native image and stored in the native image cache.
NGEN is recommended as the last step during installation process, hence the name (native) install-time code generation?
Note: Consideration on using NGen:
- NGen is important to getting faster startup through better page sharing and working set reduction
- NGen is primarily interesting for client scenarios which need the faster startup to be responsive
- NGen is not recommend for Asp.Net because the assemblies NGen produces cannot be shared between App Domains
- NGen for V1.0 and V1.1 was primarily designed for the CLR, and while it can be used for shared libraries and client apps:
Always measure to make sure it is a win for your application
Make sure your application is well behaved in the face of brittleness and servicing
CLR GC (Garbage Collection/Garbage Collector depends of the context) makes following assumptions:
The newer an object is, the shorter its lifetime will be.
The older an object is, the longer its lifetime will be.
Collecting a portion of the heap is faster than collecting the whole heap.
Generation memory threshold
There are three (0, 1, 2) generations defined by CLR. When CLR initializes, it selects a memory threshold for each generation, say 256 Kb for g0, 2 Mb for g1 and 10 Mb for g2. GC self-tunes these thresholds to give an optimal GC frequency-process time.
Generation Promotion
All objects are initially put into g0. When threshold reached, GC starts compact the garbage objects. Survival objects are promoted into g1. GC won’t compact g1 until its threshold is reached. Vice versa, objects survive g1 GC are promoted into g2. When GC compacts an older generation, it also compacts younger generation(s).
A Finalizable type implements Finalize method. GC calls this Finalize method to release the memory taken by the object. At least 2 GC processes are required to clear up finalizable objects. However, GC works on a separate thread and only starts when a generation threshold is full, there is no guarantee when Finalize will be called. This is the Non deterministic finalization.
Finalizable types should always implement Dispose Pattern to allow GC deterministically disposes it.
Finalize is non public and only called by GC. Dispose is public and called by the user of the class. Call to Dispose to cleanup an resource triggers the Finalize being called by GC later (if Finalize is implemented).
We use using(…){} on Disposable resource such like FileStream. This pattern gives a visual block of the scope that a resource will be available to use and enforce the Dispose by the close brace.
IDisposable, an Interface defines Dispose() method. It should be implemented by all objects, resources that need Finalization in a deterministic way. Richter suggests a very useful Dispose Pattern in his book.
using System;
///
public sealed class OSHandle : IDisposable{
///
private IntPtr _handle;
public OSHandle(IntPtr handle){
this._handle = handle;
}
///
/// when garbage collected, this Finalize method is called to close the
/// unmanaged resource's handle
///
~OSHandle(){
Dispose(false);
}
///
/// this public method can be called to deterministically close the unmanaged resource's handle
///
public void Dispose(){
//because the object is explicitly cleaned up, stop the garbage collector from calling the
//finalize method when this is running
GC.SuppressFinalize(this);
Dispose(true);
}
///
/// this public method can be called to deterministically close the unmanaged resource's handle
///
public void Close(){
Dispose();
}
///
/// this method is called by Finalize, Dispose and Close to do the actual cleanup
/// OSHandle is made 'sealed' and this method is 'private'to protect it from be called -
/// the derived class need to implement their own cleanup then call this one.
/// If OSHandle cannot be 'sealed' this method need to be virtual protected
///
///
private void Dispose(bool disposing){
//Synchronize threads calling Dispose/Close simutaneously
if (disposing){
//the object is being explicitly disposed of/closed, not finalized. It is therefore
//safe for code here to access fields that reference other objects because the Finalize
//method of these ther objects hasn't yet been called
//For our sample OSHandle class, we have nothing to do here
}
// The object is being disposed of/closed or finalized
if (IsValid){
// close the unmanaged resource
CloseHandle(this._handle);
// set the handle to some sentinel value. This prcaution prevents the possiblity of
// calling CloseHandle twice
this._handle = InvalidHandle;
}
}
//Make this property return an invalid value for whatever unmanaged resource you are using
public IntPtr InvalidHandle {get {return IntPtr.Zero; }}
//Public method to return the wrapped handle
public IntPtr ToHandle () {return this._handle ; }
//public implicit cast operator returns the wrapped handle
public static implicit operator IntPtr(OSHandle osHandle){
return osHandle.ToHandle();
}
public bool IsValid {get {return (this._handle != InvalidHandle);}}
public bool IsInvalid {get {return !IsValid;}}
//private method called to free the unmanaged resource
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static bool CloseHandle(IntPtr handle);
}
Pointer: Chapter 19 of Jeffery Richter’s Book Applied Microsoft .NET Framework Programming
TASKLIST [/S system [/U username [/P [password]]]] [/M [module] | /SVC | /V] [/FI filter] [/FO format] [/NH]
Description: This tool displays a list of currently running processes on either a local or remote machine.
Procedure call in out-of-proc requires object sterilization/marshalling. .Net Remoting enables out-of-proc communication in .NET.
In Windows XP and Windows 2000 (IIS5), ASP.NET is running in ASPnet_wp.exe worker process. In Windows 2003 (IIS6), ASP.NET is running in W3WP.exe worker process.
P.S. Worker Process is the Application(s) pool that web application is assigned to.
1 comment:
Thank you for your efforts.
Post a Comment