Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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.

/// <summary>
///   Tries to cast <paramref name="value" /> to an instance of type <typeparamref name="T" /> .
/// </summary>
/// <typeparam name="T"> The type of the instance to return. </typeparam>
/// <param name="value"> The value to cast. </param>
/// <param name="result"> When this method returns true, contains <paramref name="value" /> cast as an instance of <typeparamref
///    name="T" /> . When the method returns false, contains default(T). </param>
/// <returns> True if <paramref name="value" /> is an instance of type <typeparamref name="T" /> ; otherwise, false. </returns>
public static bool TryCast<T>(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 failure.
        return destinationType.IsNullable();
    }

    // Convert.ChangeType fails when the destination type is nullable.  If T is nullable we use the underlying type.
    var underlyingType = Nullable.GetUnderlyingType(destinationType) ?? destinationType;

    try
    {
        /*
         * At the moment I cannot remember why I handled Guid as a separate case, but
         * I must have been having problems with it at the time or I'd not have bothered.
         */
        if (underlyingType == typeof(Guid))
        {
            if (value is string)
            {
                value = new Guid(value as string);
            }
            if (value is byte[])
            {
                value = new Guid(value as byte[]);
            }

            result = (T)Convert.ChangeType(value, underlyingType);
            return true;
        }

        result = (T)Convert.ChangeType(value, underlyingType);
        return true;
    }
    catch (Exception ex)
    {
        // This was originally used to help me figure out why some types weren't casting in Convert.ChangeType.
        // It could be removed, but you never know, somebody might comment on a better way to do THAT to.
        var traceMessage = ex is InvalidCastException || ex is FormatException || ex is OverflowException
                                ? string.Format("The given value {0} could not be cast as Type {1}.", value, underlyingType.FullName)
                                : ex.Message;
        Trace.WriteLine(traceMessage);

        result = default(T);
        return false;
    }
}
share|improve this question
1  
If you think some of the things in your code need explaining, why didn't you include those comments in your original code? If you expect that we would be confused by your code, why do you think your coworkers wouldn't? – svick Oct 27 '12 at 20:01

1 Answer

I have a much simpler method in mind:

public static bool TryCast<T>(this object value, out T result)
{
    if (value is T)
    {
        result = (T)value;
        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<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);

And I really am against this approach:

if (underlyingType == typeof(Guid))
{
    if (value is string)
    {
        value = new Guid(value as string);
    }
    if (value is byte[])
    {
        value = new Guid(value as byte[]);
    }
    //...

If you want these kind of custom conversion features, my suggestion would be to keep your converters in a static, read-only Converter collection. A sample converter class can be like this:

public abstract class Converter
{
    // Fields
    private readonly Guid _From;
    private readonly Guid _To;

    // Properties
    public Guid From { get { return _From; } }
    public Guid To { get { return _To; } }

    // Constructors
    internal Converter(Guid from, Guid to)
    {
        _From = from;
        _To = to;
    }

    // Functions
    public abstract object Convert(object obj);
}

And you can instantiate it via this child:

public sealed class Converter<TFrom, TTo> : Converter
{
    private readonly Func<object, object> _Converter;

    public Converter(Func<TFrom, TTo> converter)
        : base(typeof(TFrom).GUID, typeof(TTo).GUID)
    {
        _Converter = o =>
        {
            if (!(o is TFrom))
                throw new ArgumentException();

            return func.Invoke((TFrom)o);
        };
    }
    public override object Convert(object obj) { return _Converter.Invoke(obj); }
}

Like this:

var converter = new Converter<string, int>(s => int.Parse(s));
// Converters should be thread-safe.
Converters.Add(converter);

So you can write your TryCast method like this:

public static bool TryCast<T>(this object value, out T result)
{
    if (value is T)
    {
        result = (T)value;
        return true;
    }

    var from = value.GetType().GUID;
    var to = typeof(T).GUID;
    var converter = Converters.FirstOrDefault(c => c.From == from && c.To == to); 
    if (converter != null)
    {
        result = (T)converter.Convert(value);
        return true;
    }

    result = default(T);
    return false;
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.