C# .NET Reflection : Obtaining a Type Object with Object.GetType()
We can obtain an instance of the Type class in a variety of ways. However, the one thing we cannot do is directly create a Type object using the new keyword, as Type is an abstract class. The Object.GetType method, the typeof operator, and various methods of the Assembly object return a Type object. GetType is a member of the Object class, which is the ubiquitous base class. Therefore, GetType is inherited by .NET types and available to all managed instances. Each instance can call GetType to return the related Type object. The typeof operator extracts a Type object directly from a reference or value type. Assembly objects have several members that return one or more Type objects. For example, the Assembly.GetTypes method enumerates and returns the Types of the target assembly.
As a member base class object, GetType is accessible to all instances of reference and values types. GetType returns the Type object of the instance. This is the syntax of the GetType method:
Type GetType()
The following code creates instances of a value and a reference type, which are passed individually as object parameters to successive calls to the DisplayType method, which homogenizes the objects, where each object loses its distinction. The function extracts the type of the instance and displays the Type name. Finally, if the Type represents a MyClass, the MyClass.Display method is called:
using System;
namespace Examples.Reflection
{
class Program
{
static void Main()
{
int value = 5;
MyClass obj = new MyClass();
DisplayType(value);
DisplayType(obj);
Console.ReadKey();
}
static void DisplayType(object parameterObject)
{
Type parameterType = parameterObject.GetType();
string name = parameterType.Name;
Console.WriteLine("Type is " + name);
if (name == "MyClass")
{
((MyClass)parameterObject).Display();
}
}
}
class MyClass
{
public void Display()
{
Console.WriteLine("MyClass > Display");
}
}
}
C# .NET Exception : AppDomain.UnhandledException
When an unhandled exception is manifested in a Console application, the AppDomain .UnhandledException is raised. Subscribe to the event to clean up the resources of the application, such as closing files and relinquishing data connections. You might also record the unhandled exception in the event log or another location. It is important to note that the exception is not caught in the AppDomain.UnhandledException handler. After the handler finishes, the unhandled exception causes the application to be terminated. The AppDomain.UnhandledException event is triggered only in the initial application domain; it is ignored in other application domains.
Subscribe to the AppDomain.UnhandledException event with an UnhandledExceptionEventHandler delegate. The delegate has two parameters. The object parameter is the AppDomain object of the initial application domain. The UnhandledExceptionEventArgs parameter contains the specifics of the unhandled exception. This is the syntax of the UnhandledExceptionEventHandler:
UnhandledExceptionEventHandler syntax:
-
void UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
UnhandledExceptionEventArgs offers the IsTerminating and ExceptionObject properties. IsTerminating is a Boolean property indicating the status of the application. If true, the application is terminating because of the exception. If false, the application survives the exception. In .NET 2.0, this property is always true. Unhandled exceptions on both managed and unmanaged threads terminate an application. This is cleaner than the .NET 1.1 unhandled exception model, where exceptions raised on managed threads were nonfatal. The Exception property is an exception object for the unhandled exception. Inexplicably, this property is an object type, not an Exception type. Cast the property to the Exception type to access the details of the exception.
In the following Console application, the OnUnhandledException method is added to the AppDomain.UnhandledException event. When the subsequent divide by zero exception occurs, the OnUnhandledException method is called.
using System;
namespace Examples.Exceptions
{
public class Program
{
public static void Main()
{
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(
OnUnhandledException);
int vara = 5, varb = 0;
vara /= varb;
Console.ReadKey();
}
public static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
string application_name = sender.ToString();
Exception except = (Exception)e.ExceptionObject;
string errormessage = "Application " + application_name + " [ Exception " + except.Message + " ]";
Console.WriteLine(errormessage);
}
}
}
Log4Net Cofiguration
I had to post this one long days ago as I promised in one of my earlier post about Log4Net. Sorry for this late. I am not a man of words. Thanks to Chris, for reminding me about this post. There are lots of things in the configuration of Log4Net. I will only about the commonly used sections.
<log4net> <appender name="LogFileAppender" type="log4net.Appender.FileAppender"> <param name="File" value="D:\Temp\ApplicationLog.log" /> <param name="AppendToFile" value="true" /> <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="" /> <param name="Footer" value="" /> <param name="ConversionPattern" value="%d [%t] %-5p %m%n" /> </layout> </appender> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender" > <layout type="log4net.Layout.PatternLayout"> <param name="Header" value="[Header]\r\n" /> <param name="Footer" value="[Footer]\r\n" /> <param name="ConversionPattern" value="%d [%t] %-5p %m%n" /> </layout> </appender> <root> <level value="DEBUG" /> <appender-ref ref="LogFileAppender" /> <appender-ref ref="ConsoleAppender" /> </root> </log4net>
Log4Net is configured in the configuration section “log4net”, specified with the tag <log4net>. Under this tag, you can find two tags, <appender> and <root>. (There could be more actually. but I did not need them yet.)
<appender> section says where and how the log should be written. It could be in the console, in a file, or database. Logs can even be sent to an email address. So, depending on you needs, you have to configure the appender. Under <log4net> element one or more appenders can be defined. I am interested to describe some of the appenders. Please remind me if I forget to do so.
<root> section is to say, which appender will be used to log, and what types of log will be written.Only one root logger element may only be defined and it must be a child of <log4net> element. To define te log-level, you have to specify your desired log-level in the <level> element. You can set the ‘value’ attribute to any of the five log-levels. The levels are as follows hierarchically:
- FATAL
- ERROR
- WARN
- INFO
- DEBUG
Once you specify the level of the log in the ‘value’ attribute, logs for that level and the levels above will be written.
C# .NET Exceptions : Application.ThreadException
In a Windows Forms application, the windows procedure raises the Application.ThreadException event upon an unhandled exception. Subscribe to the ThreadException event to handle the unhandled exception. The subscriber is an exception handler, which prevents the application from being terminated. Do not propagate an exception caught in this manner in the unhandled exception handler. The new exception is unprotected and will likely terminate the application. After the unhandled exception handler completes, execution continues at the next message in the message pump.
Subscribe to the ThreadException event with a ThreadExceptionEventHandler delegate, which has two parameters. The object parameter is the thread object of the thread that raised the exception. The ThreadExceptionEventArg parameter of the System.Threading namespace contains the exception that was unhandled. This is the signature of the ThreadExceptionEventHandler:
ThreadExceptionEventHandler syntax:
void ThreadExceptionEventHandler(object sender, ThreadExceptionEventArgs e)
In the following code, the OnThreadException handler is added to the ThreadException event. The bthException_Click method raises an unhandled divide by zero exception. The unhandled exception is then handled in the OnThreadException method, which displays an informative message. Run the application in release mode for the expected results. Otherwise, the Visual Studio debugger intercedes the exception.
private void btnException_Click(object sender, EventArgs e)
{
int vara = 5, varb = 0;
vara /= varb;
}
private void Form1_Load(object sender, EventArgs e)
{
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(OnThreadException);
}
void OnThreadException(object sender, ThreadExceptionEventArgs e)
{
Thread t = (Thread)sender;
Exception threadexception = e.Exception;
string errormessage = "Thread ID: " + t.ManagedThreadId.ToString() + " [ " + threadexception.Message + " ]";
MessageBox.Show(errormessage);
}
Why Log4Net?
Microsoft has its own application block for logging. So, why should we use Log4Net instead of that?
Log4Net has some advantages over Enterprise Library Logging Application Block. These made Log4Net more preferable.
Works with .NET 1.0 & 1.1 - The much improved logging of EntLib 2.0 and above is only available if your application is running on .NET 2.0 or greater. log4net however works on all versions of .NET.
Simpler install - When using the Enterprise Library there are some services you really should install. This is as simple as running a bat file included with EntLib but it does complicate your deployment process.
Slightly faster - log4net was significantly quicker than EntLib 1.1 logging. EntLib 2.0 onwards has improved its performance but log4net remains slightly faster. A benchmark I found while researching my presentation had EntLib taking approximately 5-6 seconds to log 100,000 entries while log4net took about 3 seconds. Does the speed difference matter? Probably not. However log4net net does support…
Appender Buffering - Buffering support with some appenders lets log4net queue up log entries and write them in a single go. If you are writing entries to the database then buffering is a good way to improve performance.
More platforms - Enterprise Library does not support the .NET Compact Framework while log4net does.
Remote Exceptions in C# .NET
Exceptions sometime occur in remote code. An exception that is raised in a different application domain is a remote exception. Remote exceptions include exceptions thrown in a .NET Remoting application or a Web service application. Exceptions that cross application domains must be serialized to maintain the state. System exceptions are serializable. However, you need to make application exceptions serializable.
Follow these steps to serialize an application exception:
-
Adorn the application exception with the serializable attribute.
-
Implement a two-argument constructor with a SerializationInfo and StreamingContext parameter. Deserialize the exception with the SerializationInfo parameter, which is a state bag. Retrieve state information of the exception using the Get methods of the SerializationInfo object. The StreamingContext parameter provides additional data about the source or target of the serialization process. In addition, call the same constructor in the base class to allow the base class to deserialize its state.
-
Implement the GetObjectData method to serialize the exception. The method also has two parameters: Serialization and StreamingContext. Use the Serialization.AddValue to serialize the state of the exception. Invoke GetObjectData on the base class to allow it to serialize itself.
-
For the exception to be available in the client assembly, share the assembly through a global assembly cache or an application configuration file. If the assembly is not shared, the assembly must be copied into the private directory of the client application.
CustomException is an application exception that supports remoting. There is one property, prop_Time, which is serialized in GetObjectData and deserialized in the two-argument constructor:
using System;
using System.Reflection;
using System.Runtime.Serialization;
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyCultureAttribute("")]
namespace Examples.Exceptions
{
[Serializable]
public class CustomException : Exception
{
public CustomException() : base("custom exception", null)
{
prop_Time = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToShortTimeString();
}
protected CustomException(SerializationInfo info, StreamingContext context) : base(info, context)
{
prop_Time = info.GetString("Time");
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Time", prop_Time, typeof(string));
base.GetObjectData(info, context);
}
protected string prop_Time = null;
public string Time
{
get
{
return prop_Time;
}
}
}
}
In Microsoft Visual Studio, the assembly attributes, such as AssemblyVersion, are found in the AssemblyInfo.cs file.
Application Exceptions in C# .NET
Application exceptions are custom exceptions and are thrown by the application, not by the CLR. Application exceptions are derived from System.ApplicationException or System.Exception. System.ApplicationException adds nothing to System.Exception. While System.SystemException is a marker for system exceptions, System.ApplicationException brands application exceptions. A custom exception derived from System.Exception accomplishes the same feat. When several custom exceptions are planned, create a custom base exception class to categorize the exceptions. For convenience and maintainability, deploy application exceptions together in a separate assembly.
Do not create an application exception for an existing exception. Research the available system exceptions to avoid replicating an existing exception.
These are the steps for creating an application exception:
-
Name the application exception. As a best practice, the class name should have the Exception suffix, as in ApplicationException.
-
Derive the application exception from System.Exception.
-
Define constructors that initialize the state of the application exception. This includes initializing members inherited from the base class.
-
Within the application exception, refine System.Exception as desired, such as by adding attributes that further delineate this specific exception.
To raise an application exception, use the throw statement. You can also throw system exceptions. Thrown exceptions are considered software exceptions. The CLR treats software exceptions as standard exceptions.
throw syntax:
throw exceptioninstance1;
throw2;
The second syntax is specialized: It is available in the catch block, but nowhere else. This version of the throw statement rethrows as an exception caught in the catch block. However, the best policy is to add additional context to an exception before propagating the exception object. Propagating exceptions is reviewed later in this chapter.
Application exceptions are typically prompted by an exceptional event. What is an exceptional event? A strict definition does not exist. You define the basis of the event using whatever criteria are appropriate. Remember, raising an exception simply for transfer of control or a nonexceptional event is bad policy. In an application, the following could be considered exceptional events where throwing an application exception is warranted:
-
Constructor fails to initialize the state of an object.
-
A property does not pass validation.
-
Null parameters.
-
An exceptional value is returned from a function.
ConstructorException is an application exception. Throw this exception when a constructor fails. It refines the System.Exception base class with name of the type and time of exception. In addition, the Message property is assigned a congruous message. This is the code for the ConstructorException class:
using System;
namespace Examples.Exceptions
{
public class ConstructorException : Exception
{
public ConstructorException(object origin) : this(origin, null)
{
}
public ConstructorException(object origin, Exception innerException) : base("Exception in constructor", innerException)
{
prop_Typename = origin.GetType().Name;
prop_Time = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToShortTimeString();
}
protected string prop_Typename = null;
public string Typename
{
get
{
return prop_Typename;
}
}
protected string prop_Time = null;
public string Time
{
get
{
return prop_Time;
}
}
}
}
This code uses the ConstructorException class:
using System;
namespace Examples.Exceptions
{
public class Program
{
public static void Main()
{
try
{
ZClass obj = new ZClass();
}
catch (ConstructorException except)
{
Console.WriteLine(except.Message);
Console.WriteLine("Typename: " + except.Typename);
Console.WriteLine("Occured: " + except.Time);
}
Console.ReadKey();
}
}
class ZClass
{
public ZClass()
{
// initialization fails
throw new ConstructorException(this);
}
}
}
C# .NET System.Exception Properties
System.Exception has a full complement of attributes providing information on the exception. The following table describes the properties of the Exception class.
|
Property |
Description |
Type |
Read/Write |
|
Data |
Returns a dictionary collection that provides optional user-defined details of exception. |
IDictionary |
R |
|
HelpLink |
Link to a help file for the exception. |
string |
R/W |
|
HResult |
The HRESULT, which is a 32-bit error code common to COM, assigned to the exception. This is a protected property. |
int |
R/W |
|
InnerException |
When exceptions are propagated, the inner exception represents the previous exception. |
Exception |
R |
|
Message |
User-friendly message describing the exception. |
string |
R |
|
Source |
Name of application or object where exception occurred. |
string |
R/W |
|
StackTrace |
String representation of the call stack when the exception occurred. |
string |
R |
|
TargetSite |
Reference to the method where exception is raised. |
MethodBase |
R |
The Message and InnerException properties are settable in constructors of the Exception class.
The following code uses some of the properties of the Exception class. In Main, MethodA is called, and an exception is raised. The exception is caught and handled in Main. In the catch block, the exception flag is set to false. Leveraging the TargetSite property, MethodA is then called again successfully. The TargetSite property returns a MethodBase type, which can be used to late bind and invoke a method.
using System;
using System.Reflection;
namespace Example.Exceptions
{
public class Starter
{
public static bool bException = true;
public static void Main()
{
try
{
MethodA();
}
catch (Exception except)
{
Console.WriteLine(except.Message);
bException = false;
except.TargetSite.Invoke(null, null);
Console.ReadKey();
}
}
public static void MethodA()
{
if (bException)
{
throw new ApplicationException("exception message");
}
}
}
}
C# .NET System.Exception Functions
System.Exception has four constructors:
- public Exception1()
- public Exception2(string message)
- public Exception3(string message, Exception innerException)
- protected Exception4(Serialization info, StreamingContext context)
Exception1 is the default constructor. Exception2 constructor sets the user-friendly message of the exception. Exception3 constructor also sets the inner exception, which is the originating exception. Exception4 deserializes an exception raised remotely.
The Exception class has several other helpful functions. The table lists the important methods of the class.
|
Table: Exception Methods |
|
|
Method Name |
Result |
|
GetBaseException |
Returns the root exception in a chain of exception objects |
|
GetObjectData |
Serializes data of the Exception class |
|
GetType |
Returns the type of the exception |
|
ToString |
Concatenates the name of the exception object with the user-friendly message |
The following code calls GetBaseException and outputs the error message of the initial exception. If the current exception is the first exception in a chain of exceptions, GetBaseException returns null. Alternatively, you can walk InnerException properties back to the first exception.
using System;
namespace Example.Exceptions
{
public class Program
{
public static void Main()
{
try
{
MethodA();
}
catch (Exception except)
{
Exception original = except.GetBaseException();
Console.WriteLine(original.Message);
}
Console.ReadKey();
}
public static void MethodA()
{
try
{
MethodB();
}
catch (Exception except)
{
throw new ApplicationException("Inner Exception", except);
}
}
public static void MethodB()
{
throw new ApplicationException("Innermost Exception");
}
}
}
C# .NET Exceptions : Propagating Exceptions
Exceptions are not always handled locally where the exception is caught. It is sometimes beneficial to catch an exception and then propagate the exception. Propagating an exception is catching and then rethrowing the exception. Rethrowing an exception continues the search along the call stack to find an appropriate handler. Here are some reasons to propagate an exception:
-
There is a centralized handler for the exception. There are several reasons for implementing centralized handlers, including code reuse. Instead of handling an exception in various locations in an application, concentrate code for certain exceptions in a centralized handler. Wherever the exception is raised, the proper response is to record the exception and then delegate to the centralized handler. A central handler can be used to handle all exceptions in a single place.
-
Resources required to handle the exception are not available locally. For example, an exception is raised because of an invalid database connection. However, the correct connection string is read from a file not available where the exception occurs. The solution is to propagate the exception to a handler that has access to the file resource.
-
Propagate unwanted exceptions caught in the umbrella of the exception filter. This would be useful for catching all DataException types with the exception of the DuplicateNameException. One solution would be to write 12 individual catches—one for each of the data exceptions except for the DuplicateNameException exception. A better solution is to catch the DataException type and propagate the DuplicateNameException when necessary. This is one catch statement versus 12 catch statements and eliminates redundant code.
-
Catch an exception to gather information or to report on an exception, and then propagate the exception.
To propagate an exception, rethrow the same exception or another exception in the catch block. An empty throw statement propagates the caught exception. Alternatively, throw a different exception.
Exceptions might propagate through several layers of an application. Ultimately, the exception could percolate to the user interface level. As an exception percolates, the exception becomes less specific. Exceptions from the lower echelon of an application contain detailed information appropriate to the application developer, but probably not relevant to the user. Internal exceptions might contain security and other sensitive information not appropriate for a benign (or malicious) user. Record the facts of the internal exception if logging is preferable. Exceptions that reach the user should present user-relevant information: a user-friendly message, steps to resolve the exception, or even a customer support link.
When an original exception is rethrown, you can preserve the that exception in the InnerException attribute. Successive InnerException attributes form a chain of exceptions from the current exception to the original exception. The InnerException can be initialized in the constructor of the new exception. Here is sample code that propagates an exception and sets the inner exception:
using System;
namespace Examples.Exceptions
{
public class Program
{
public static void Main()
{
try
{
MethodA();
}
catch (Exception except)
{
Console.WriteLine(except.Message);
}
}
public static void MethodA()
{
try
{
MethodB();
}
catch (DivideByZeroException inner)
{
// record divide by zero exception in event log.
// except is inner exception
throw new Exception("Math exception", inner);
}
}
public static void MethodB()
{
int var1 = 5, var2 = 0;
var1 /= var2;
}
}
}
-
Archives
- February 2009 (1)
- November 2008 (6)
- October 2008 (4)
- September 2008 (13)
- August 2008 (11)
- July 2008 (29)
- June 2008 (19)
- May 2008 (8)
-
Categories
-
RSS
Entries RSS
Comments RSS

