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: Stack trace of the second version of the code
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
} // end handler
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
} // end handler
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,
Collapse | Copy Code
[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 => book.Length == lengthOfTheBookName).Select(book => book);
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()
{
// most of the code has been removed for simplicity
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.
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
{
// Fields
public int lengthOfTheBookName;
// Methods
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
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.
No comments:
Post a Comment