Reputation: 3802
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.
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
value = (T)items[typeof(T)];
crashes becauseCannot convert type to 'T'
Is it possible to fix it?
Upvotes: 2
Views: 172
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
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