Question3r
Question3r

Reputation: 3802

parse generic type to other generic type

I created a custom dictionary that takes Type as key and an interface as a value. I want to identify the interfaces by their type and there should only exist one of them in that collection. I achieved this by creating this code

internal class ComponentCollection
{
    public ComponentCollection()
    {
        components = new Dictionary<Type, IComponent>();
    }

    private Dictionary<Type, IComponent> components;

    public void AddComponent<T>(T component) where T : IComponent
    {
        components.Add(typeof(T), component as IComponent);
    }

    public void RemoveComponent(Type componentType)
    {
        components.Remove(componentType);
    }

    public bool TryGetComponent<T>(out T component)
    {
        try
        {
            component = (T)components[typeof(T)];
            return true;
        }
        catch (Exception)
        {
            component = default(T);
            return false;
        }
    }
}

and I noticed I can use it for other code places too. I need to replace IComponent with a generic type. I started with this

internal class TypeCollection<V>
{
    public TypeCollection()
    {
        items = new Dictionary<Type, V>();
    }

    private Dictionary<Type, V> items;

    public void Add<T>(T value) where T : V
    {
        items.Add(typeof(T), value as V);
    }

    public void Remove(Type type)
    {
        items.Remove(type);
    }

    public bool TryGetValue<T>(out T value)
    {
        try
        {
            value = (T)items[typeof(T)];
            return true;
        }
        catch (Exception)
        {
            value = default(T);
            return false;
        }
    }
}

but two problems come up.

  1. items.Add(typeof(T), value as V); crashes because

The type parameter cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint

  1. value = (T)items[typeof(T)]; crashes because

Cannot convert type to 'T'

Is it possible to fix it?

Upvotes: 2

Views: 172

Answers (2)

p.s.w.g
p.s.w.g

Reputation: 149020

Since you have the constraint where T : V on Add you don't need to use as.

public void Add<T>(T value) where T : V
{
    items.Add(typeof(T), value);
}

And this also hints at what you need to do to TryGetValue; add a constraint:

public bool TryGetValue<T>(out T value) where T : V
{
    try
    {
        value = (T)items[typeof(T)];
        return true;
    }
    catch (Exception)
    {
        value = default(T);
        return false;
    }
}

Note also that you can implement TryGetValue without a try catch, using:

public bool TryGetValue<T>(out T value) where T : V
{
    if (items.TryGetValue(typeof(T), out var foundValue))
    {
        value = (T)foundValue;
        return true;
    }

    value = default(T);
    return false;
}

Or even more succinctly (but arguably less readable):

public bool TryGetValue<T>(out T value) where T : V
{
    var found = items.TryGetValue(typeof(T), out var foundValue);
    value = found ? (T)foundValue : default(T);
    return found;
}

Upvotes: 2

Eric Lippert
Eric Lippert

Reputation: 660159

If you are absolutely, positively sure that the thing you're getting really is a T, then you can cast from any type to a generic type parameter via:

T t = (T)(object)whatever;

However, if T is a value type then this introduces a boxing penalty, which can cause both space and time performance issues. Track your performance carefully to see if there is a measurable, meaningful impact on this code path.

And as always, when unboxing you must make sure that the types match exactly. You cannot for example unbox a boxed integer to double, even though you can convert an unboxed integer to double.

Upvotes: 2

Related Questions