patterncsharpMajor
TryCast<T> method
Viewed 0 times
methodtrycaststackoverflow
Problem
This isn't urgent, it is more along the lines of trivia or a challenge. The solution works fine as it is, but I suspect it could be better.
What follows is a method I came up with a while back in a rather ugly situation where I need to make a "best effort" to try casting an object of an unrestricted unknown type TO an unrestricted unknown type. The code has just been bugging me. It seems that there should be a more elegant way to do this, but I wanted to get that "Best Effort" part right.
The methods follows the "Try" convention. It accepts an object "value" and an out param of type T, "result." It attempts to cast value into result as type T. If it succeeds, it returns true. If it cannot, it sets result = default(T) and returns false.
It feels like I'm going to lots of trouble in the method. I'd be open to suggestions to streamline this a bit. Comments included here are mostly not in the original... just to explain why a few things were done the way they were done.
```
///
/// Tries to cast to an instance of type .
///
/// The type of the instance to return.
/// The value to cast.
/// When this method returns true, contains cast as an instance of . When the method returns false, contains default(T).
/// True if is an instance of type ; otherwise, false.
public static bool TryCast(this object value, out T result)
{
var destinationType = typeof(T);
var inputIsNull = (value == null || value == DBNull.Value);
/*
* If the given value is null, we'd normally set result to null and be done with it.
* HOWEVER, if T is not a nullable type, then we can't REALLY cast null to that type, so
* TryCast should return false.
*/
if (inputIsNull)
{
// If T is nullable, this will result in a null value in result.
// Otherwise this will result in a default instance in result.
result = default(T);
// If the input is null and T is nullable, we report success. Otherwise we report failu
What follows is a method I came up with a while back in a rather ugly situation where I need to make a "best effort" to try casting an object of an unrestricted unknown type TO an unrestricted unknown type. The code has just been bugging me. It seems that there should be a more elegant way to do this, but I wanted to get that "Best Effort" part right.
The methods follows the "Try" convention. It accepts an object "value" and an out param of type T, "result." It attempts to cast value into result as type T. If it succeeds, it returns true. If it cannot, it sets result = default(T) and returns false.
It feels like I'm going to lots of trouble in the method. I'd be open to suggestions to streamline this a bit. Comments included here are mostly not in the original... just to explain why a few things were done the way they were done.
```
///
/// Tries to cast to an instance of type .
///
/// The type of the instance to return.
/// The value to cast.
/// When this method returns true, contains cast as an instance of . When the method returns false, contains default(T).
/// True if is an instance of type ; otherwise, false.
public static bool TryCast(this object value, out T result)
{
var destinationType = typeof(T);
var inputIsNull = (value == null || value == DBNull.Value);
/*
* If the given value is null, we'd normally set result to null and be done with it.
* HOWEVER, if T is not a nullable type, then we can't REALLY cast null to that type, so
* TryCast should return false.
*/
if (inputIsNull)
{
// If T is nullable, this will result in a null value in result.
// Otherwise this will result in a default instance in result.
result = default(T);
// If the input is null and T is nullable, we report success. Otherwise we report failu
Solution
For casting, I have a much simpler method in mind:
You don't need to detect
The following writes nothing because
Lastly, the following should write two lines that are "test 1" and "test 2".
I'm really against this approach:
If you want this kind of custom conversion features, my suggestion would be to keep your converters in a static, thread-safe
And the implementation is:
To initialize it:
With support for the custom converters, the final
public static bool TryCast(this object obj, out T result)
{
if (obj is T)
{
result = (T)obj;
return true;
}
result = default(T);
return false;
}You don't need to detect
nullable types manually since is operator already checks for it: 5 is int? returns true, so the following code writes 5 to the console.int value = 5;
int? result;
if (value.TryCast(out result))
Console.WriteLine(result);The following writes nothing because
TryCast returns false.string value = "5";
int? test;
if (value.TryCast(out test))
Console.WriteLine(test);Lastly, the following should write two lines that are "test 1" and "test 2".
var list = new List();
list.Add("test 1");
list.Add("test 2");
IEnumerable enumerable;
if (list.TryCast(out enumerable))
foreach (var item in enumerable)
Console.WriteLine(item);I'm really against this approach:
if (underlyingType == typeof(Guid))
{
if (value is string)
{
value = new Guid(value as string);
}
else if (value is byte[])
{
value = new Guid(value as byte[]);
}
//...If you want this kind of custom conversion features, my suggestion would be to keep your converters in a static, thread-safe
Converter collection. A sample converter class can be like this:public abstract class Converter
{
private readonly Type from; // Type of the instance to convert.
private readonly Type to; // Type that the instance will be converted to.
// Internal, because we'll provide the only implementation...
// ...that's also why we don't check if the arguments are null.
internal Converter(Type from, Type to)
{
this.from = from;
this.to = to;
}
public Type From { get { return this.from; } }
public Type To { get { return this.to; } }
public abstract object Convert(object obj);
}And the implementation is:
// Sealed, because this is meant to be the only implementation.
public sealed class Converter : Converter
{
Func converter; // Converter is strongly typed.
public Converter(Func converter)
: base(typeof(TFrom), typeof(TTo)) // Can't send null types to the base.
{
if (converter == null)
throw new ArgumentNullException("converter", "Converter must not be null.");
this.converter = converter;
}
public override object Convert(object obj)
{
if (!(obj is TFrom))
{
var msg = string.Format("Object is not of the type {0}.", this.From.FullName);
throw new ArgumentException(msg, "obj");
}
// Can throw exception, it's ok.
return this.converter.Invoke((TFrom)obj);
}
}To initialize it:
var int32ToString = new Converter(i => i.ToString());
var stringToInt32 = new Converter(s => int.Parse(s));
// Converters should be a thread-safe collection of our abstract `Converter` type.
Converters.Add(int32ToString);
Converters.Add(stringToInt32);With support for the custom converters, the final
TryCast method becomes this:public static bool TryCast(this object obj, out T result)
{
if (obj is T)
{
result = (T)obj;
return true;
}
// If it's null, we can't get the type.
if (obj != null)
{
var converter = Converters.FirstOrDefault(c =>
c.From == obj.GetType() && c.To == typeof(T));
// Use the converter if there is one.
if (converter != null)
try
{
result = (T)converter.Convert(obj);
return true;
}
catch (Exception)
{
// Ignore - "Try*" methods don't throw exceptions.
}
}
result = default(T);
return false;
}Code Snippets
public static bool TryCast<T>(this object obj, out T result)
{
if (obj is T)
{
result = (T)obj;
return true;
}
result = default(T);
return false;
}int value = 5;
int? result;
if (value.TryCast(out result))
Console.WriteLine(result);string value = "5";
int? test;
if (value.TryCast(out test))
Console.WriteLine(test);var list = new List<string>();
list.Add("test 1");
list.Add("test 2");
IEnumerable<string> enumerable;
if (list.TryCast(out enumerable))
foreach (var item in enumerable)
Console.WriteLine(item);if (underlyingType == typeof(Guid))
{
if (value is string)
{
value = new Guid(value as string);
}
else if (value is byte[])
{
value = new Guid(value as byte[]);
}
//...Context
StackExchange Code Review Q#17982, answer score: 30
Revisions (0)
No revisions yet.