Contents
- Introduction
- How does it work?
- throw an exceptionObject
- Where and Select of Enumerable
- Limitations
- History
- Reference
Source code and demo
Introduction
In the first part of How does it work in C#? article I discussed about the var, auto-implemented properties and += and -= of events in C#. In this article, I will be discussing about throwing an exception and how does it work and what is the internal mechanism for these, what is internal working mechanism of where and select clause of Enumerable class in C#.
How does it work?
In the following discussion we will see the internal working mechanism about the throw statement and where, select of Enumerable class.
throw an exceptionObject
Exception is one of the common scenarios for any application. As a result, proper exception management is one of the important tasks in application development. In here, I would like to discuss about throw statement in C#, how does throw anExceptionObject; or just throw works. I wrote a small program which will help me to explain the details. The task of this program is simple, it will raise an exception and throw it,
namespace TestHarnessPart2
{
using System;
class Program
{
static void Main(string[] args)
{
try
{
Person person = new Person();
}
catch (Exception exceptionObject)
{
throw;
}
}
}
public class Person
{
public Person()
{
throw new Exception("Exception from Person.");
}
}
}
The above code catch the exception and just throw it with out passing any explicit exception object.I created another version of this above code for example,
namespace TestHarnessPart2
{
using System;
class Program
{
static void Main(string[] args)
{
try
{
Person person = new Person();
}
catch (Exception exceptionObject)
{
throw exceptionObject;
}
}
}
public class Person
{
public Person()
{
throw new Exception("Exception from Person.");
}
}
}
The above code will create a Person object and if any exception occurred during this creation it will catch that exception and from the catch block it throws the catched exception which is throw exceptionObject;. Now if we see the stack trace of those above code we will find out there is difference, stack trace for first version of the code is as below,
Fig: Stack trace of the first version of the code.
And stack trace of the second version of the code,
Fig: Stack trace of the second version of the code
From the above two images we can see the details of the stack trace is different.Now the question is how does it happens, to get the answer we will get help of ILDasm program, grab the exe of TestHarnessPart2 from the bin folder and drop into the ILDasm program , for the first version of the code we will see something like below,
Fig :IL code for using just throw in the catch block.
From the above image we can see exceptionObject has been defined as local variable using .locals init [1] but in the catch block compiler change the throw statement into
catch [mscorlib]System.Exception
{
IL_000b: stloc.1
IL_000c: nop
IL_000d: rethrow
}
rethrow, which means compiler does not change the original stack trace of the exception object it is re- throwing the existing one, where as if we look into the following image for the second version of the above C# code,
Fig: IL code for using just throw excetpionObject in the catch block.
We can see exceptionObject has been defined as local variable as well, using the same way for the first version of the code for example, .locals init [1]. But in the catch block it is doing totally different stuffs compare to first version of the above code. In the catch block compiler load location of 1( ldloc.1) which is the exceptionObject
catch [mscorlib]System.Exception
{
IL_000b: stloc.1
IL_000c: nop
IL_000d: ldloc.1
IL_000e: throw
}
and throw that one. As a result, this exceptionObject will not hold all the stack trace raised earlier except the stack trace from this current state. Now the last bit of the puzzle is, what actually throw and rethrow statement does? From the Partition III CIL.doc retrieved from ECMA C# and Common Language Infrastructure Standards we can see what actually throw and rethrow does,
IL Instruction |
Description |
throw |
Throw an exception. The throw instruction throws the exception object (type O) on the stack and empties the stack. So in relation to the second version of the code block it will be,
[1] class [mscorlib]System.Exception
exceptionObject
|
rethrow |
Rethrow the current exception. The rethrow instruction is only permitted within the body of a catch handler. It throws the same exception that was caught by this handler. A rethrow does not change the stack trace in the object. |
So it is clear that throw exceptionObject override the stack trace where as, just throw statement does not override the stack trace.
Where and Select of Enumerable
Before we start discussing about Where and Select, just have a quick look of the where and select clause’s signature,
Fig: Signature of the Where clause
Fig: Signature of the Select clause
Where and Select is two extension methods of Enumerable class in .Net. Following sections will discuss about the internal of Where and Select, how does it work. Before, we go ahead just bit of heads up about Func in .Net. According to MSDN, we can use this delegate to represent a method that can be passed as a parameter without explicitly declaring a custom delegate. The encapsulated method must correspond to the method signature that is defined by this delegate. A simple example of Func below,
namespace TestHarnessPart2
{
using System;
public class ExampleOfFunc
{
public int TestFunc(string dataToCheck, Func<string,> getLength)
{
return getLength(dataToCheck);
}
}
}
and to test the above code,
private static void TestFuncExample()
{
ExampleOfFunc exampleOfFunc = new ExampleOfFunc();
Console.WriteLine(exampleOfFunc.TestFunc("Example of Func", (dataToTest) => dataToTest.Length));
}
So from the above code we can see TestFunc method accepting a Func as parameter and execute it just by doing return getLength(dataToCheck);, from the caller of this method is actually passing an anonymous method block for the Func parameter. Through out the entire discussion about where and select we will find this concept used many places,
Before we go ahead I like to show a bit of code created to explain the where and select statement,
namespace TestHarnessPart2
{
using System.Collections.Generic;
using System.Linq;
class WhereSelect
{
List<string> bookList = new List<string>() { "Einstein: His Life and Universe", "Ideas And Opinions", "The World As I See It " };
public IEnumerable<string> GetBookListWhichLengthIsGreaterThan(int lengthOfTheBookName)
{
return bookList.Where(book =>
{
var currentBook = book;
return book.Length > lengthOfTheBookName;
}).Select(book =>
{
var currentBook = book;
return book;
});
}
}
}
The above code is not doing much rather check the booklist whether it has book which name length is greater than given length (lengthOfTheBookName). Now we will try to find out bit of internal working mechanism of Where and Select clause, where and select clause is defined in the Enumerable class and internal of the where clause is as below,
Fig: Where internal
From the above image we see Where method is calling another internal private class named WhereListIterator<T> which is doing all the work for us which is filter the booklist List based on the condition provided and return output.
private class WhereListIterator<tsource> : Enumerable.Iterator<tsource>
{
public override bool MoveNext()
{
while (this.enumerator.MoveNext())
{
TSource current = this.enumerator.Current;
if (this.predicate(current))
{
base.current = current;
return true;
}
}
}
}
From the above code we can see this.predicate(current)) line of code is actually executing the Func or anonymous method block in this case, <GetBookListWhichLengthIsGreaterThan>b__0 method. On the other hand, select clause is working like below,
Fig: Select internal
Also, from the above image we can see that there is one selector which is actually <GetBookListWhichLengthIsGreaterThan>b__1
method to select the item from the list. Now need to find out where is this <GetBookListWhichLengthIsGreaterThan>b__1 and
<GetBookListWhichLengthIsGreaterThan>b__0
coming from? Please have a look image below, from the following image we can there is two method named <GetBookListWhichLengthIsGreaterThan>b__1 used for selector and <GetBookListWhichLengthIsGreaterThan>b__0 used for predicate of the where clause.
Fig: The output of WhereSelect test
Where and Select clause is using those two methods for doing the operation. Bit of more investigation to find out about it. Now we need the help of .Net Reflector and ILDasm. First grab the TestHarnessPart2.exe from the bin folder and drop into .Net Reflector, then we will find out following interesting stuff, a class <>c__DisplayClass3 with following details,
[CompilerGenerated]
private sealed class <>c__DisplayClass3
{
public int lengthOfTheBookName;
public bool <GetBookListWhichLengthIsGreaterThan><getbooklistwhichlengthisgreaterthan>b__0(string book)
{
string currentBook = book;
return (book.Length > this.lengthOfTheBookName);
}
}
From the class we can see a method named <GetBookListWhichLengthIsGreaterThan>b__0 which is actually containing the filtering condition we defined in the where clause,
{
var currentBook = book;
return book.Length > lengthOfTheBookName;
}
And also there is also another method (outside of <>c__DisplayClass3 class) named <GetBookListWhichLengthIsGreaterThan>b__1
[CompilerGenerated]
private static string <GetbookListWhichLengthIsGreaterThan><getbooklistwhichlengthisgreaterthan>b__1(string book)
{
string CS$<>8__locals4 = book;
return book;
}
Which is actually containing the statement we defined for the select clause as below,
{
var currentBook = book;
return book;
}
Now to prove the above stuff, we will look at the IL code retrieved from ILDasm(also grab the TestHarnessPart2.exe from the bin folder and drop into ILDasm program) for the above C# code,
Fig: IL code of the GetBookListWhichLengthIsGreaterThan method
From the above image we can see where clause is using <GetBookListWhichLengthIsGreaterThan>b__0
method of <>c__DisplayClass3
class and select clause is using <GetBookListWhichLengthIsGreaterThan>b__1 method. So it is clear now how does it work.
Limitation
- There are no discussions about Query Expression and Lambda Expression.
History
Reference