I'm currently working through a series of bugs in an application. Our application is written in C#/ASP.NET (on the server) and HTML/CSS/JavaScript (on the client). We are using ELMAH to log any uncaught exceptions in the system so that we may identify it later and fix it, which is what I'm currently doing.
The way ELMAH works is to log any uncaught exception, so once you catch and handle the exception, it can be omitted from ELMAH.
This article closely relates to a question I asked here on Stack Overflow.
To give a little bit of insight into this article, ELMAH logs uncaught exceptions, but sometimes you have certain scenarios where the exception is thrown to the browser and handled there. ELMAH doesn't know that it's being handled elsewhere, so it still logs the exception.
In our application, we use WCF web services to send JSON data back to the client. Say for example one of those services fails and throws an exception, we send that exception back to the browser and handle it there (and as stated, ELMAH still logs it).
So I had this idea, which might only apply to a small set of use cases, but as an idea, I wanted to know what the community thought of it.
As OO programmers know, methods/functions have a return type (or void
).
Example
public void SayHello() { Print("Hello"); } public string GetMyName() { return "Joe Bloggs"; }
All well and good so far, but what if I wanted my method/function to return either a value, or an exception if one was to occur? I can do this in C# using the dynamic keyword.
Example
public dynamic GetMyNameOrCryLikeABaby() { try { return DoSomethingWrong(); // might throw an exception, but should return a string. } catch(Exception ex) { return ex; } }
Personally I don't like this approach. I don't think it is a good use of the dynamic keyword, and it involves the developer adding in additional code to check whether the return value was (in this example) a string, or an Exception.
The idea I came up with to get around this was based somewhat on .NET's Nullable<T>
structure. The idea here being that a ValueType wrapped in a Nullable<T>
(e.g. Nullable<int>
) can return null.
The structure below has some interesting characteristics! The aim was to write a structure that could implicitly be cast to the expected type, but could also return an exception, should one occur. This structure is called "Exceptable" meaning it might hold an exception.
Exceptable<T>
Source Code
/// <summary>
/// Represents a generic type that can also hold an associated exception.
/// </summary>
/// <typeparam name="T">The type of this Exceptable object.</typeparam>
public struct Exceptable<T>
{
/// <summary>
/// Gets or sets the value of this object.
/// </summary>
public T Value { get; set; }
/// <summary>
/// Gets or sets an exception that may occur when using this type.
/// </summary>
public Exception Exception { get; set; }
/// <summary>
/// Gets a value indicating whether this object has a value.
/// </summary>
public Boolean HasValue
{
get { return this.Value != null; }
}
/// <summary>
/// Gets a value indicating whether this object has an exception.
/// </summary>
public Boolean HasException
{
get
{
return this.Exception != null;
}
}
/// <summary>
/// Allows implicit casting between a raw type, and an Exceptable type.
/// </summary>
/// <param name="value">The exceptable value to convert to a raw type.</param>
/// <returns>A raw type</returns>
public static implicit operator T(Exceptable<T> value)
{
return value.Value;
}
/// <summary>
/// Allows implicit casting between an Exceptable type and a raw type.
/// </summary>
/// <param name="value">The raw type to convert to an excepable type.</param>
/// <returns>An excepable type.</returns>
public static implicit operator Exceptable<T>(T value)
{
return new Exceptable<T>
{
Value = value
};
}
}
So as you can see here, it wraps the expected type into Exceptable
as a generic type and provides implicit conversion between Exceptable<T>
and T
.
Use Cases
Exceptable<string> name = "Joe Bloggs";
Exceptable<int> age = 26;
public static Exceptable<string> GetMyNameOrCryLikeABaby()
{
Exceptable<string> result = String.Empty;
try
{
result = DoSomethingWrong() // might throw an exception but should return a string.
}
catch(Exception ex)
{
result.Exception = ex;
}
return result;
}
string name = GetMyNameOrCryLikeABaby();
Exceptable<string> nameOrException = GetMyNameOrCryLikeABaby();
As you can see from the examples above, I can assign values directly to Exceptable<T>
. I can also use it as a return type for a method, and due to implicit conversion, I can choose whether to return just the associated value of the structure, or the structure itself, in case I want to handle the associated exception.
What do you think?
3 Answers 3
Doing this instead of working with exceptions the usual way (
throw
andcatch
) is a big code smell. I think I would need a really good justification for this and I'm not sure slightly improved logging is that. I would try looking more into other possibilities, like filteringException
s based on their type, or something like that.This code comes close to reinventing the
Exception
monad. You might want to study that for some ideas.Your type should protect its invariants. If it's supposed to contain a value or an exception, but not both, you shouldn't allow the user to use the type the wrong way.
A similar principle also applies to reading: if a value is not present,
Value
should throw an exception, instead of silently returning the default value. The same probably applies toException
.For a simple type like this, it often makes sense to make it immutable. Also, mutable value types are evil, you should never use them without a really good reason (and I don't see that reason here).
You should be very careful with implicit conversions. An implicit conversion should never lead to loss of information and it should be always valid. In your case, that's not true for the case when
Exceptable
contains an exception, because then the implicit conversion silently returns the default value.So, if you want to have a conversion from
Exceptable<T>
toT
, it should be explicit and it should throw when an exception is present.You're checking
Value
againstnull
. This is a bad idea for several reasons:In some cases,
null
can be a legitimate return value that does not indicate an error. Your type won't handle that correctly.If
T
is a non-nullable value type, then the default value is notnull
, soHasValue
won't work correctly. If your type is meant to support only reference types, you should make that explicit by adding the constraintwhere T : class
to it.
The name "exceptable" is not great. Though I'm not sure what would be a better name.
-
\$\begingroup\$ Thanks for this response. Based on your comments, I'm feeling that there is very little scope to use this...so maybe worth scrapping (but leaving here for future reference). \$\endgroup\$Matthew Layton– Matthew Layton2013年07月22日 14:48:34 +00:00Commented Jul 22, 2013 at 14:48
-
\$\begingroup\$ @svick - Item #7 - How about "Exceptionable" ? \$\endgroup\$Matthew Layton– Matthew Layton2013年07月22日 16:04:41 +00:00Commented Jul 22, 2013 at 16:04
I suggest you don't need methods like HasValue()
& HasException()
hardcoded in it as they can be checked by the caller when receiving an response or may create an extension to it later to make it extendable.
But as this is for logging then you can add Source
and Message
which can be useful.So Info
& Trace
has a place called Message
and not just Exception
So you can simple define Exceptable
as
public class Exceptable<T>
{
//The T Result will be set as actual response
public T Value { get; private set; }
//Message will hold any message even exception message
public string Message { get; set; }
//InnerException to hold the exception and return back to the caller
public Exception Exception { get; set; }
//Source will be the source method who is returning the value like class.method
public string Source { get; set; }
public static implicit operator T(Exceptable<T> value)
{
return value.Value;
}
public static implicit operator Exceptable<T>(T value)
{
return new Exceptable<T>
{
Value = value
};
}
}
Then it can be used as
public static Exceptable<string> GetMyNameOrCryLikeABaby(bool throwException)
{
Exceptable<string> result = String.Empty;
try
{
if(throwException) throw new Exception("some exception");
result = "Joe Bloggs";
}
catch(Exception ex)
{
result.Exception = ex;
}
return result;
}
Exceptable<int> age = 26;
Exceptable<string> name = GetMyNameOrCryLikeABaby(false);
Exceptable<string> nameOrException = GetMyNameOrCryLikeABaby(true);
Then you can created an extension to it later
public static class ExceptableExtensions
{
public static bool IsValueEmpty(this Exceptable<string> exp)
{
if(exp.Value == string.Empty)
return true;
return false;
}
public static bool IsValueNull(this Exceptable<string> exp)
{
if(exp.Value == null)
return true;
return false;
}
public static bool IsLessThen(this Exceptable<int> exp, int checkvalue)
{
if(exp.Value < checkvalue)
return true;
return false;
}
public static bool HasException<T>(this Exceptable<T> exp)
{
if(exp.Exception != null)
return true;
return false;
}
}
and use it as per your code.
age.IsLessThen(50);
name.HasException();
I think that you are setting yourself up for horrible spaghetti code down the line if you don't catch and throw exceptions like the Lord intended.
If you absolutely INSIST on returning an error, I would suggest adding two OUT parameters to the methods:
string myFunkyFunction(string arg, out bool hasException, out string exceptionMessage/*or you could pass an actual Exception if that's your cup of tea*/)
{
try{
//code here
}
catch(Exception ex)
{
hasException = true;
exceptionMessage = ex.Message;
}
if(!hasException)
{
hasException = false;
exceptionMessage = "";
}
return "something";
}
then, IF there is an exception, do something.
-
\$\begingroup\$ I think it's a bad idea to use just
string
instead ofException
,Exception
can contain quite a lot useful data. And thehasException
parameter is not useful, the exception parameter should benull
if there is no exception (certainly not""
) and checking that is enough. \$\endgroup\$svick– svick2013年07月22日 13:12:22 +00:00Commented Jul 22, 2013 at 13:12 -
\$\begingroup\$ Well, yes, Exception is more useful, which is why I put that in a comment. Also, Visual Studio will complain if you don't assign something to an out parameter. \$\endgroup\$Captain Kenpachi– Captain Kenpachi2013年07月22日 14:34:38 +00:00Commented Jul 22, 2013 at 14:34
-
\$\begingroup\$ I meant that having
out Exception exception
would be better thanstring
. And you have to assign something, but that something can benull
. \$\endgroup\$svick– svick2013年07月22日 15:17:01 +00:00Commented Jul 22, 2013 at 15:17 -
\$\begingroup\$ fair enough. I did mention the option of returning an exception in the comment to the right of the method signature. \$\endgroup\$Captain Kenpachi– Captain Kenpachi2013年07月22日 15:18:09 +00:00Commented Jul 22, 2013 at 15:18
Explore related questions
See similar questions with these tags.
bind
, or anything similar. \$\endgroup\$