Search code examples
c#genericscastingoftype

Safely cast a Generic type in C#


I have a piece of code that attempts to cast and return an object called m_obj depending on a given generic type. Here is the code:

private IObject m_obj;
public bool TryGetAs<T>(out T value) where T :IObject
{
    value = m_obj as T; // The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint

    // value = (T)m_obj; This compiles!

    if (value != null)
        return true;

    return false;
}

I know that if m_obj were a list, I could do this:

private readonly IList<Object> m_objects;

internal IList<T> Collect<T>() where T : IObject
{
    return m_objects.OfType<T>().ToList();
}

However, I cannot figure out how to solve the problem in the first code example. Here is the original code again:

public interface IObject
{
    
}

This is my proposed solution:

    public bool TryGetAs<T>(out T value) where T :IObject
    {
        value = default(T);
        if (!(m_obj is T))
            return false;

        value = (T)m_obj;
        return true;
    }

I would like to know if my solution is correct and if there are any better or more elegant ways to solve this problem.


Solution

  • The as operator is reserved for reference types. If T is always a reference type, the solution is to add a class constraint to TryGetAs<T>. If T can also be a value type, this is not an option. In this case you can use the is operator:

    public bool TryGetAs<T>(out T value) where T : IObject
    {
        if(m_obj is T)
        {
            value = (T)m_obj;
            return true;
        }
        else
        {
            value = default(T);
            return false;
        }
    }
    

    In C# 7.0 you can simplify it like this (and improve performance since you don't need an is cast and then another type cast):

    public bool TryGetAs<T>(out T value) where T : IObject
    {
        if(m_obj is T tValue)
        {
            value = tValue;
            return true;
        }
        else
        {
            value = default(T);
            return false;
        }
    }