When dealing with custom exceptions, I usually inherit from Exception and then add some fields/properties to my exception class to store some additional info:
public class MyException : Exception
{
public int ErrorCode{get;set;}
public MyException()
{}
}
In the above example, the ErrorCode
value is stored in the exception, meaning that I have to add it to and retireve if from the SerializationInfo
object in the protected constructor and the overridden GetObjectData
method.
The Exception class has a Data property, which according to MSDN:
Gets a collection of key/value pairs that provide additional user-defined information about the exception.
If I store the error code inside the Data
, it will get serialised for me by the Exception class (according to Reflector), meaning that my exception class now looks like:
public class MyException : Exception
{
public int ErrorCode
{
get {return (int) Data["ErrorCode"];}
set {Data["ErrorCode"] = value;}
}
public MyException()
{}
}
This means that whilst there is a bit more work to do in dealing with the get/set of the error code (like dealing with casting errors and situations where the error code might not be in the dictionary), I don't have to worry about serialising/deserialising it.
Is this just two different ways of achieving the same thing, or does one way have any clear advantage(s) over the other (apart from those I've already mentioned)?
If you are bothering to create your own exception, you don't need the Data property. Data comes in useful when you want to store a bit of extra information in an existing exception class, but don't want to create your own custom exception class.
I would avoid using Data as it is not under your control e.g. some code somewhere might decide to overwrite the "ErrorCode" value. Instead use the propery and implement serialization. I use the following code to test all my custom exceptions to make sure I've implemented them properly.
public static void TestCustomException<T>() where T : Exception
{
var t = typeof(T);
//Custom exceptions should have the following 3 constructors
var e1 = (T)Activator.CreateInstance(t, null);
const string message = "message";
var e2 = (T)Activator.CreateInstance(t, message);
Assert.AreEqual(message, e2.Message);
var innerEx = new Exception("inner Exception");
var e3 = (T)Activator.CreateInstance(t, message, innerEx);
Assert.AreEqual(message, e3.Message);
Assert.AreEqual(innerEx, e3.InnerException);
//They should also be serializable
var stream = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(stream, e3);
stream.Flush();
stream.Position = 0;
var e4 = (T)formatter.Deserialize(stream);
Assert.AreEqual(message, e4.Message);
Assert.AreEqual(innerEx.ToString(), e4.InnerException.ToString());
}
Microsoft's own guidelines:
Do make exceptions serializable. An exception must be serializable to work correctly across application domain and remoting boundaries.
I would store it in the Data-property which sadly would let outside code modify the value without consent, or use solution 1 (in your example) but make it serializeable. In the end I would probably go for solution 1 so I can be sure that the value never changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With