Wednesday, 27 May 2015

C# and the CLR. Compilation & GC, threads, asyncrhonous programming, reflection and interoperability

JIT Compilation and Garbage Collection

GC cleans up unused memory (on heap, i.e. no refs to memory location). Visits global variables and local variables to determine what is in use.

Frees up and compresses memory - defrag for memory - and fixes refs of objects moved in memory.

You can use the GC problematically via  a class GC with static members and properties. sssss CollectionCount(0), (1), (2) reflect the three generations

When an object is first created it is in generation 0.  If it survives a GC, i.e. still referenced when the GC runs, then they are promoted to generation 1.   If it survives another GC then it is promoted to GC 2.   Generation 0 is  GC'd much more frequently that generations 1 and 2.  Many objects are short lived (with scope only for  method) and hence stay in generation 0 so it is GC'd more frequently.

Threads

System.Threading - low level API for starting, stopping and joining threads
System.Threading.Tasks - high level API for concurrent and asynchronous programming. (New .net 4)

using System.Threading.Tasks
string[] urls = {"http://website1.com", "http://website2.com", "http://website3.com"};

Parallel.ForEach(urls, url => {
   var client = new WebClient();
   var result = client.DownloadString(url);
   // ...
});

Parallel is a higher level API

Asynchronous

WCF Service proxies and other libraries such as the web client class have asynchronous versions of methods.  These are preferable generally as they can use threads from the thread pool and easily recycle them, rather than having to create a new thread.

Low Level API System.Threading


using System.Threading
string[] urls = {"http://website1.com", "http://website2.com", "http://website3.com"};

foreach (var url in urls)
{
    Download(url);
}

private static void Download( string url)
{
  var client = new WebClient();
  client.DownloadStringCompleted +=  client_DownloadStringCompleted;
  client.DownloadStringAsync(new Uri(url), url);
}


public static void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    var html = e.Result;
    var url = e.UserState as string;
    //Do something here
}

You need to wire up an event handler to DownloadStringCompleted to execute when the thread has finished downloading the string.  The compiler can figure out the type of event handler so you just need to give it the name of the method to execute when ready.

The second parameter in DownloadStringAsync is a user token that you can use to "tunnel" additional information, such as the url being downloaded, into the event handler.

You don't need to fiddle around creating threads, and could potentially reuse a thread if one task completes very quickly.  But your code is splattered through three methods.

High Level API System.Threading.Tasks

Parallel can invoke something directly, or you can iterate over a collection with a .ForEach.  Provide the collection and an expression to tell the task parallel library what to do (using a lambda expression).

using System.Threading.Tasks
string[] urls = {"http://website1.com", "http://website2.com", "http://website3.com"};

Parallel.ForEach(urls, url => {
   var client = new WebClient();
   var result = client.DownloadString(url.ToString());
   // ...
});

However the main thread will block until all parallel tasks complete. (The lower level async coding didn't block).

Reflection and Metadata

Reading metadata of the assembly / code running by reflecting on itself.  Typically starts by getting a reference to a System.Type object using either (at run time) the .GetType method available on every object or (at compile time) the TypeOf operator.

With this type reference you can query the type to get info, e.g. properties, members, etc.  Most only return public members/properties by default.  There is an overloaded version that allows you to pass in binding flags (an enumeration) you can OR together.

using System.Reflection;

static class Program
{
    static void Main(string[] args)
    {
         var type = typeof (Program);
          var members = type.GetMembers(BindingFlags.Static |
                                                                 BindingFlags.Instance | 
                                                                 BindingFlags.NonPublic |
                                                                 BindingFlags.Public);
        foreach (Var memberInfo in members)
        {
              Console.WriteLine(memberInfo.Name);
        }
    }
}

Can also look at attributes (DataAnnotations) on a member or property.  They are baked into the assembly by the compiler.
Attributes are very useful so you can create your own - custom metadata
Your custom attribute is a public class that derives from the System.Attribute class.  Naming convention is to end the name of your class with Attribute.   Specify usage with an attribute [AttributeUssage(AttributesTargets.) enumeration.  You can create a constructor and also define public Properties as appropriate.

To use, just decorate the target thus
[YourAttribute{any, input, params)]


Any input params in the constructor must be provided when using the attribute.  Unlike a class, however, you can additionally provide any additional public properties available in the custom attribute by specifying its name.

You can not only inspect objects using reflection you can create objects and invoke methods and properties too.

Get a reference to a type.
Get a propertyinfo for one of the type's properties.
Get the value of that property held by an object of that type.  It returns an object so cast it as needed.

Could also use GetMethod to get the MethodInfo of a method available on a type.
Invoke the same.  Any input params for the method are passed as an object[].

dynamic keyword
Bypasses any static type checking by the compiler.  It also coerces the returned type into what you want.  The result is cleaner code for the same functionality.  

dynamic someObject = o;
int length = someObject.length;

However there is the risk of a runtime error if the property or method can't be found on the object.

Create Objects using the activator class.
Get a reference to a type
var type = o.GetType();
var newInstance = Activator.CreateInstance(type)

Can create code on the fly!  You create a new DynamicMethod, passing in the name, its return type and any input parameters.  Then you lay out the instructions to execute within the method.  To do this you write IL code by calling the ILGenerator class to load in OpCodes.  Not C#, these are IL instructions.

Processor architecture

There are two CLRs - 32bit or 64bit.  32bit can run on a 64 bit system (as a 32 bit process via emulator), but not vice-versa.  Set these by setting build properites on the project.

x86 platform = 32 bit.
AnyCPU will load your program into the best type of process available.  So on a 32 bit machine it will run your program in a 32 bit process.  On a 64bit platform it will run your progam in a 64 bit process.

The default in VS for projects that create .exe executables is x86 i.e. 32 bit as these can run in most places.   x64 can only run on a 64 bit process (but does have a greater memory address available)

The default in VS for class projects or projects that create DLL assemblies is AnyCPU so that it can load into any executable, i.e. an executable running in either 32bit or 64 bit.

For AnyCPU, if you build an executable under this and load it into a 64 bit process it will run, but if it attempts to load a 32bit dependency this will fail.  You can't load a 32bit library into a running 64 bit process.

Ineroperability

COM was a historical foundation building block of older windows operations.
Mark up code with interop attributes that COM needs.  Then run Assembly registrtation tool (regasm) to register information into the registry for the assembly, so that COM programs can access and use your code.

You can also consume COM components by adding a reference to the COM component.
Less and less important as most functionality in COM is now available in .Net.

Unmanaged code not packaged into a COM component.
Use Platform Invoke - PInvoke - to call into Windows APIs and unmanaged code.

See wiki http://pinvoke.net to see what signatures, etc needed to invoke an API.

No comments:

Post a Comment