I'm trying to run the following code but get a casting error.
How can I rewrite my code to achive the same ?
boolResult= (bool?)dataReader["BOOL_FLAG"] ?? true;
intResult= (int?)dataReader["INT_VALUE"] ?? 0;
Thanks
Use the "IsDbNull" method on the data reader... for example:
bool? result = dataReader.IsDbNull(dataReader["Bool_Flag"]) ? null : (bool)dataReader["Bool_Flag"]
Edit
You'd need to do something akin to:
bool? nullBoolean = null;
you'd have
bool? result = dataReader.IsDbNull(dataReader["Bool_Flag"]) ? nullBoolean : (bool)dataReader["Bool_Flag"]
Consider doing it in a function.
Here's something I used in the past (you can make this an extension method in .net 4):
public static T GetValueOrDefault<T>(SqlDataReader dataReader, System.Enum columnIndex)
{
int index = Convert.ToInt32(columnIndex);
return !dataReader.IsDBNull(index) ? (T)dataReader.GetValue(index) : default(T);
}
Edit
As an extension (not tested, but you get the idea), and using column names instead of index:
public static T GetValueOrDefault<T>(this SqlDataReader dataReader, string columnName)
{
return !dataReader.IsDBNull(dataReader[columnName]) ? (T)dataReader.GetValue(dataReader[columnName]) : default(T);
}
usage:
bool? flag = dataReader.GetValueOrDefault("BOOL_COLUMN");
There's an answer here that might be helpful:
https://stackoverflow.com/a/3308515/1255900
You can use the "as" keyword. Note the caution mentioned in the comments.
nullableBoolResult = dataReader["BOOL_FLAG"] as bool?;
Or, if you are not using nullables, as in your original post:
boolResult = (dataReader["BOOL_FLAG"] as bool?) ?? 0;
bool? boolResult = null;
int? intResult = null;
if (dataReader.IsDBNull(reader.GetOrdinal("BOOL_FLAG")) == false)
{
boolResult = dataReader.GetBoolean(reader.GetOrdinal("BOOL_FLAG"));
}
else
{
boolResult = true;
}
if (dataReader.IsDBNull(reader.GetOrdinal("INT_VALUE")) == false)
{
intResult= dataReader.GetInt32(reader.GetOrdinal("INT_VALUE"));
}
else
{
intResult = 0;
}
I'm sure I found the inspiration for this somewhere around the interweb but I can't seem to find the original source anymore. Anyway, below you find a utility class which allows to define an extension method on DataReader, like this:
public static class DataReaderExtensions
{
public static TResult Get<TResult>(this IDataReader reader, string name)
{
return reader.Get<TResult>(reader.GetOrdinal(name));
}
public static TResult Get<TResult>(this IDataReader reader, int c)
{
return ConvertTo<TResult>.From(reader[c]);
}
}
Usage:
reader.Get<bool?>("columnname")
or
reader.Get<int?>(5)
Here's the enabling utility class:
public static class ConvertTo<T>
{
// 'Factory method delegate', set in the static constructor
public static readonly Func<object, T> From;
static ConvertTo()
{
From = Create(typeof(T));
}
private static Func<object, T> Create(Type type)
{
if (!type.IsValueType) { return ConvertRefType; }
if (type.IsNullableType())
{
return (Func<object, T>)Delegate.CreateDelegate(typeof(Func<object, T>), typeof(ConvertTo<T>).GetMethod("ConvertNullableValueType", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(new[] { type.GetGenericArguments()[0] }));
}
return ConvertValueType;
}
// ReSharper disable UnusedMember.Local
// (used via reflection!)
private static TElem? ConvertNullableValueType<TElem>(object value) where TElem : struct
{
if (DBNull.Value == value) { return null; }
return (TElem)value;
}
// ReSharper restore UnusedMember.Local
private static T ConvertRefType(object value)
{
if (DBNull.Value != value) { return (T)value; }
return default(T);
}
private static T ConvertValueType(object value)
{
if (DBNull.Value == value)
{
throw new NullReferenceException("Value is DbNull");
}
return (T)value;
}
}
EDIT: makes use of the IsNullableType() extension method defined like so:
public static bool IsNullableType(this Type type)
{
return
(type.IsGenericType && !type.IsGenericTypeDefinition) &&
(typeof (Nullable<>) == type.GetGenericTypeDefinition());
}
Here's my shot at an extension method. Column name semantics and falls back to default(T) when a null is encountered.
public static class DbExtensions
{
public static T ReadAs<T>(this IDataReader reader, string col)
{
object val = reader[col];
if (val is DBNull)
{
// Use the default if the column is null
return default(T);
}
return (T)val;
}
}
Here is the sample usage. Remember that despite string being a reference type, it will still fail to cast to null from a DBNull. The same is true with int?.
public Facility Bind(IDataReader reader)
{
var x = new Facility();
x.ID = reader.ReadAs<Guid>("ID");
x.Name = reader.ReadAs<string>("Name");
x.Capacity = reader.ReadAs<int?>("Capacity");
x.Description = reader.ReadAs<string>("Description");
x.Address = reader.ReadAs<string>("Address");
return x;
}
using extension method:
public static T GetValueOrDefault <T> (this SqlDataReader reader, string column) {
var isDbNull = reader[column] == DBNull.Value;
return !isDbNull ? (T) reader[column] : default (T);
}
Remember that a DBNull is not the same thing as null, so you cannot cast from one to the other. As the other poster said, you can check for DBNull using the IsDBNull() method.
Try this version. It performs some basic conversion and manages default values as well.
Related
I'm trying to write a "simple" Generic Get<T>; extension for
System.Runtime.MemoryCache.
Why "simple" ? Because generally I know object's real type before caching it, so when I retrieve it from cache, I'm not going to convert it in unpredictable ways.
For example: if boolean "true" value is stored in cache with cacheKey "id", so
Get<string>("id") == "true";
Get<int>("id") == 1; // any result > 0 is okay
Get<SomeUnpredictableType> == null; // just ignore these trouble conversions
Here's my incomplete implemention:
public static T DoGet<T>(this MemoryCache cache, string key) {
object value = cache.Get(key);
if (value == null) {
return default(T);
}
if (value is T) {
return (T)value;
}
// TODO: (I'm not sure if following logic is okay or not)
// 1. if T and value are both numeric type (e.g. long => double), how to code it?
// 2. if T is string, call something like Convert.ToString()
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
Any suggestions are highly appreciated.
===================================
Update (04/11/2016):
For those nice suggestions given, I implement my first version of Get<T>
public class MemCache {
private class LazyObject<T> : Lazy<T> {
public LazyObject(Func<T> valueFactory) : base(valueFactory) { }
public LazyObject(Func<T> valueFactory, LazyThreadSafetyMode mode) : base(valueFactory, mode) { }
}
private static T CastValue<T>(object value) {
if (value == null || value is DBNull) {
return default(T);
}
Type valType = value.GetType();
if (valType.IsGenericType && valType.GetGenericTypeDefinition() == typeof(LazyObject<>)) {
return CastValue<T>(valType.GetProperty("Value").GetValue(value));
}
if (value is T) {
return (T)value;
}
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(t) && typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
private MemoryCache m_cache;
public T Get<T>(string key) {
return CastValue<T>(m_cache.Get(key));
}
public void Set<T>(string key, T value, CacheDependency dependency) {
m_cache.Set(key, value, dependency.AsCacheItemPolicy());
}
public T GetOrAdd<T>(string key, Func<T> fnValueFactory, CacheDependency dependency) {
LazyObject<T> noo = new LazyObject<T>(fnValueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
LazyObject<T> old = m_cache.AddOrGetExisting(key, noo, dependency.AsCacheItemPolicy()) as LazyObject<T>;
try {
return CastValue<T>((old ?? noo).Value);
} catch {
m_cache.Remove(key);
throw;
}
}
/* Remove/Trim ... */
}
The essential work is to write a CastValue<T> to convert any object to desired type. And it doesn't have to handle very complicate condition because object types in cache is predictable for the programmer. And here's my version.
public static T CastValue<T>(object value) {
if (value == null || value is DBNull) {
return default(T);
}
if (value is T) {
return (T)value;
}
Type t = typeof(T);
t = (Nullable.GetUnderlyingType(t) ?? t);
if (typeof(IConvertible).IsAssignableFrom(t) && typeof(IConvertible).IsAssignableFrom(value.GetType())) {
return (T)Convert.ChangeType(value, t);
}
return default(T);
}
Proposal:
public static T DoGet<T>(this MemoryCache cache, string key)
{
object value = cache.Get(key);
if (value == null) {
return default(T);
}
// support for nullables. Do not waste performance with
// type conversions if it is not a nullable.
var underlyingType = Nullable.GetUnderlyingType(t);
if (underlyingType != null)
{
value = Convert.ChangeType(value, underlyingType);
}
return (T)value;
}
Usage (supposed you have an id of type int in the cache):
int id = Get<int>("id");
int? mayBeId = Get<int?>("id");
string idAsString = Get<int?>("id")?.ToString();
double idAsDouble = (double)Get<int>("id");
I haven't test it.
If I have code similar to the following:
while(myDataReader.Read())
{
myObject.intVal = Convert.ToInt32(myDataReader["mycolumn"] ?? 0);
}
It throws the error:
Object cannot be cast from DBNull to other types.
defining intVal as a nullable int is not an option. Is there a way for me to do the above?
Here's one more option:
while (myDataReader.Read())
{
myObject.intVal = (myDataReader["mycolumn"] as int? ?? 0);
}
Can you use an extension method? (written off the top of my head)
public static class DataReaderExtensions
{
public static T Read<T>(this SqlDataReader reader, string column, T defaultValue = default(T))
{
var value = reader[column];
return (T)((DBNull.Value.Equals(value))
? defaultValue
: Convert.ChangeType(value, typeof(T)));
}
}
You'd use it like:
while(myDataReader.Read())
{
int i = myDataReader.Read<int>("mycolumn", 0);
}
Can you simply use Int32.Tryparse?
int number;
bool result = Int32.TryParse(myDataReader["mycolumn"].ToString(), out number);
According to the MSDN, number will contain 0 if the conversion failed
How about something like:
object x = DBNull.Value;
int y = (x as Int32?).GetValueOrDefault(); //This will be 0
Or in your case:
int i = (myDataReader["mycolumn"] as Int32?).GetValueOrDefault();
Why not use something other than the null coalescing operator (DBNull.Value != null):
int i = myDataReader["mycolumn"] == DBNull.Value ?
Convert.ToInt32(myDataReader["mycolumn"]) :
0;
You could always wrap it up in a neat extension method:
public static T Read<T>(this DataReader reader, string column, T defaultVal)
{
if(reader[column] == DBNull.Value) return defaultVal;
return Convert.ChangeType(reader[column], typeof(T));
}
Nope, only works for nulls.
How about an extension method on object that checks for DBNull, and returns a default value instead?
//may not compile or be syntactically correct! Just the general idea.
public static object DefaultIfDBNull( this object TheObject, object DefaultValue )
{
if( TheObject is DBNull )
return DefaultValue;
return TheObject;
}
The following generic static method takes a string and returns an enum.
It nicely ignores case since I set the ignoreCase parameter to true.
However, I also want to test if the enum exists, but the enum.IsDefined method to do this doesn't seem to have an ignoreCase parameter.
How can I test if the enum is defined or not and at the same ignore case?
using System;
namespace TestEnum2934234
{
class Program
{
static void Main(string[] args)
{
LessonStatus lessonStatus = StringHelpers.ConvertStringToEnum<LessonStatus>("prepared");
ReportStatus reportStatus = StringHelpers.ConvertStringToEnum<ReportStatus>("finished");
Console.WriteLine(lessonStatus.ToString());
Console.WriteLine(reportStatus.ToString());
Console.ReadLine();
}
}
public static class StringHelpers
{
public static T ConvertStringToEnum<T>(string text)
{
if (Enum.IsDefined(typeof(T), text)) //does not have ignoreCase parameter
return (T)Enum.Parse(typeof(T), text, true);
else
return default(T);
}
}
public enum LessonStatus
{
Defined,
Prepared,
Practiced,
Recorded
}
public enum ReportStatus
{
Draft,
Revising,
Finished
}
}
public enum MyEnum
{
Bar,
Foo
}
class Program
{
static void Main(string[] args)
{
var containsFoo = Enum.GetNames(typeof(MyEnum)).Any(x => x.ToLower() == "foo");
Console.WriteLine(containsFoo);
}
}
Along with #Darin's answer, in .NET 4.0, the Enum type now has a TryParse method:
MyEnum result;
Enum.TryParse("bar", true, out result);
The important thing to remember is that there is a fundamental difference in the behaviour of Parse vs TryParse. Parse methods will throw exceptions. TryParse methods will not. This is quite important to know if you are potentially trying to parse many items.
You might be able to get away with simply using Enum.TryParse, as others have said.
However, if you want a more robust/general conversion that allows you to convert more than just strings, then you need to also use Enum.IsDefined, which unfortunately, as you found, is not case-insensitive.
Enum.TryParse is (can be) case-insensitive. But unfortunately, it allows out-of-range ints to get through!
So the solution is to use them together (and the order is important).
I wrote an extension method that does just that. It allows conversion from string, int/int?, and any other Enum/Enum? type like so:
string value1 = "Value1";
Enum2 enum2 = value1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == value1);
Enum1 enum1 = Enum1.Value1;
enum2 = enum1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == enum1.ToString());
int value2 = 1;
enum2 = value2.ParseToEnum<Enum2>();
Debug.Assert(enum2.GetHashCode() == value2);
Here's the heart of the method. This is the conversion part that answers your question. The variable value is of type object because of the "overloads" I have that take different types as the main input (see above), but you can do this with a variable of type string just fine if that's all you want (obviously changing value.ToString() to just value).
if (value != null)
{
TEnum result;
if (Enum.TryParse(value.ToString(), true, out result))
{
// since an out-of-range int can be cast to TEnum, double-check that result is valid
if (Enum.IsDefined(typeof(TEnum), result.ToString()))
{
return result;
}
}
}
There's a lot more to my extension method... it allows you to specify defaults, handles out-of-range ints just fine, and is fully case-insensitive. I can post more of it if anybody's interested.
Use Enum.TryParse instead:
T val;
if(Enum.TryParse(text, true, out val))
return val;
else
return default(T);
I'm using Compact Framework 3.5, and:
Enum.TryParse
...doesn't exist. It does have:
Enum.IsDefined
..but that doesn't support the ignoreCase parameter.
I'd like the best of both worlds, so came up with this (as a helper method)...
public bool TryParse<TEnum>(string value, bool ignoreCase, ref TEnum result) where TEnum : struct
{
bool parsed;
try
{
result = (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
parsed = true;
}
catch { }
return parsed;
}
HTH
enum DaysCollection
{
sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
public bool isDefined(string[] arr,object obj)
{
bool result=false;
foreach (string enu in arr)
{
result = string.Compare(enu, obj.ToString(), true) == 0;
if (result)
break;
}
return result;
}
private void button1_Click(object sender, EventArgs e)
{
object obj = "wednesday";
string[] arr = Enum.GetNames(typeof(DaysCollection)).ToArray();
isDefined(arr,obj);
}
Make text same case as enum string:
enum FileExts
{
jpg,
pdf
}
if (Enum.IsDefined(typeof(T), text.tolower())) //does not have ignoreCase parameter
return (T)Enum.Parse(typeof(T), text, true);
else
return default(T);
I had a similar concern and used a combination of both the .Enum.TryPase (with the case-insensitive flag set as true) and Enum.IsDefined. Consider the following as a simplification to your helper class:
public static class StringHelpers
{
public static T ConvertStringToEnum<T>(string text)
{
T result;
return Enum.TryParse(text, true, out result)
&& Enum.IsDefined(result.ToString())
? result
: default(T);
}
}
And while we're at it, since the helper class is static and the method is static - we could make this an extension method on string.
public static class StringExtensions
{
public static TEnum ToEnum<TEnum>(this string text)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
TEnum result = default(TEnum);
return !string.IsNullOrWhiteSpace(text)
&& Enum.TryParse(text, true, out result)
&& Enum.IsDefined(typeof(TEnum), result.ToString())
? result
: default(TEnum);
}
}
Here, I created a .NET Fiddle that clearly demonstrates this.
First use Enum.TryParse method to get an object of type T, then pass that object to Enum.IsDefined method:
private static T ConvertStringToEnum<T>(string stringValue) where T : struct
{
if (System.Enum.TryParse(stringValue, out T result))
{
if (System.Enum.IsDefined(typeof(T), result) || result.ToString().Contains(","))
return result;
throw new System.Exception($"{stringValue} is not an underlying value of the {typeof(T).FullName} enumeration.");
}
throw new System.Exception($"{stringValue} is not a member of the {typeof(T).FullName} enumeration.");
}
public static T ConvertStringToEnum<T>(string text)
{
T returnVal;
try
{
returnVal = (T) Enum.Parse( typeof(T), text, true );
}
catch( ArgumentException )
{
returnVal = default(T);
}
return returnVal;
}
I have the following methods in an enum helper class (I have simplified it for the purpose of the question):
static class EnumHelper
{
public enum EnumType1 : int
{
Unknown = 0,
Yes = 1,
No = 2
}
public enum EnumType2 : int
{
Unknown = 0,
Dog = 1,
Cat = 2,
Bird = 3
}
public enum EnumType3
{
Unknown,
iPhone,
Andriod,
WindowsPhone7,
Palm
}
public static EnumType1 ConvertToEnumType1(string value)
{
return (string.IsNullOrEmpty(value)) ?
EnumType1.Unknown :
(EnumType1)(Enum.Parse(typeof(EnumType1), value, true));
}
public static EnumType2 ConvertToEnumType2(string value)
{
return (string.IsNullOrEmpty(value)) ?
EnumType2.Unknown :
(EnumType2)(Enum.Parse(typeof(EnumType2), value, true));
}
public static EnumType3 ConvertToEnumType3(string value)
{
return (string.IsNullOrEmpty(value)) ?
EnumType3.Unknown :
(EnumType3)(Enum.Parse(typeof(EnumType3), value, true));
}
}
So the question here is, can I trim this down to an Enum extension method or maybe some type of single method that can handle any type. I have found some examples to do so with basic enums but the difference in my example is all the enums have the Unknown item that I need returned if the string is null or empty (if no match is found I want it to fail).
Looking for something like the following maybe:
EnumType1 value = EnumType1.Convert("Yes");
// or
EnumType1 value = EnumHelper.Convert(EnumType1, "Yes");
One function to do it all... how to handle the Unknown element is the part that I am hung up on.
Edit: Adjusted one of the enums to not be defined with integers. So I can guarantee that 0 will always be the case but Unknown will always be the correct text... I guess I could use the same example as the T(0) but do another parse on the text "Unknown".
Use this, assuming that Unknown is always the 0 value.
public static T ConvertToEnum<T>(this string value) where T : new()
{
if( !typeof(T).IsEnum )
throw new NotSupportedException( "T must be an Enum" );
try
{
return (T)Enum.Parse(typeof(T), value);
}
catch
{
return default(T); // equivalent to (T)0
//return (T)Enum.Parse(typeof(T), "Unknown"));
}
}
Usage:
EnumType2 a = "Cat".ConvertToEnum<EnumType2>();
EnumType2 b = "Person".ConvertToEnum<EnumType2>(); // Unknown
EDIT By OP (Kelsey): Your answer lead me to the correct answer so I thought I would include it here:
public static T ConvertTo<T>(this string value)
{
T returnValue = (T)(Enum.Parse(typeof(T), "Unknown", true));
if ((string.IsNullOrEmpty(value) == false) &&
(typeof(T).IsEnum))
{
try { returnValue = (T)(Enum.Parse(typeof(T), value, true)); }
catch { }
}
return returnValue;
}
use generics... something like this....
public static TResult ConvertTo<TResult>( this string source )
{
if( !typeof(TResult).IsEnum )
{
throw new NotSupportedException( "TResult must be an Enum" );
}
if (!Enum.GetNames(typeof(TResult)).Contains(source))
return default(TResult);
return (TResult)Enum.Parse( typeof(TResult), source );
}
(the code came from here)
I am trying to combine a bunch of similar methods into a generic method. I have several methods that return the value of a querystring, or null if that querystring does not exist or is not in the correct format. This would be easy enough if all the types were natively nullable, but I have to use the nullable generic type for integers and dates.
Here's what I have now. However, it will pass back a 0 if a numeric value is invalid, and that unfortunately is a valid value in my scenarios. Can somebody help me out? Thanks!
public static T GetQueryString<T>(string key) where T : IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
What if you specified the default value to return, instead of using default(T)?
public static T GetQueryString<T>(string key, T defaultValue) {...}
It makes calling it easier too:
var intValue = GetQueryString("intParm", Int32.MinValue);
var strValue = GetQueryString("strParm", "");
var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // eg use today's date if not specified
The downside being you need magic values to denote invalid/missing querystring values.
I know, I know, but...
public static bool TryGetQueryString<T>(string key, out T queryString)
What about this? Change the return type from T to Nullable<T>
public static Nullable<T> GetQueryString<T>(string key) where T : struct, IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
Convert.ChangeType() doesn't correctly handle nullable types or enumerations in .NET 2.0 BCL (I think it's fixed for BCL 4.0 though). Rather than make the outer implementation more complex, make the converter do more work for you. Here's an implementation I use:
public static class Converter
{
public static T ConvertTo<T>(object value)
{
return ConvertTo(value, default(T));
}
public static T ConvertTo<T>(object value, T defaultValue)
{
if (value == DBNull.Value)
{
return defaultValue;
}
return (T) ChangeType(value, typeof(T));
}
public static object ChangeType(object value, Type conversionType)
{
if (conversionType == null)
{
throw new ArgumentNullException("conversionType");
}
// if it's not a nullable type, just pass through the parameters to Convert.ChangeType
if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
// null input returns null output regardless of base type
if (value == null)
{
return null;
}
// it's a nullable type, and not null, which means it can be converted to its underlying type,
// so overwrite the passed-in conversion type with this underlying type
conversionType = Nullable.GetUnderlyingType(conversionType);
}
else if (conversionType.IsEnum)
{
// strings require Parse method
if (value is string)
{
return Enum.Parse(conversionType, (string) value);
}
// primitive types can be instantiated using ToObject
else if (value is int || value is uint || value is short || value is ushort ||
value is byte || value is sbyte || value is long || value is ulong)
{
return Enum.ToObject(conversionType, value);
}
else
{
throw new ArgumentException(String.Format("Value cannot be converted to {0} - current type is " +
"not supported for enum conversions.", conversionType.FullName));
}
}
return Convert.ChangeType(value, conversionType);
}
}
Then your implementation of GetQueryString<T> can be:
public static T GetQueryString<T>(string key)
{
T result = default(T);
string value = HttpContext.Current.Request.QueryString[key];
if (!String.IsNullOrEmpty(value))
{
try
{
result = Converter.ConvertTo<T>(value);
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
You can use sort of Maybe monad (though I'd prefer Jay's answer)
public class Maybe<T>
{
private readonly T _value;
public Maybe(T value)
{
_value = value;
IsNothing = false;
}
public Maybe()
{
IsNothing = true;
}
public bool IsNothing { get; private set; }
public T Value
{
get
{
if (IsNothing)
{
throw new InvalidOperationException("Value doesn't exist");
}
return _value;
}
}
public override bool Equals(object other)
{
if (IsNothing)
{
return (other == null);
}
if (other == null)
{
return false;
}
return _value.Equals(other);
}
public override int GetHashCode()
{
if (IsNothing)
{
return 0;
}
return _value.GetHashCode();
}
public override string ToString()
{
if (IsNothing)
{
return "";
}
return _value.ToString();
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
public static explicit operator T(Maybe<T> value)
{
return value.Value;
}
}
Your method would look like:
public static Maybe<T> GetQueryString<T>(string key) where T : IConvertible
{
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
return new Maybe<T>();
}
}
return new Maybe<T>();
}
I like to start with a class like this
class settings
{
public int X {get;set;}
public string Y { get; set; }
// repeat as necessary
public settings()
{
this.X = defaultForX;
this.Y = defaultForY;
// repeat ...
}
public void Parse(Uri uri)
{
// parse values from query string.
// if you need to distinguish from default vs. specified, add an appropriate property
}
This has worked well on 100's of projects. You can use one of the many other parsing solutions to parse values.