0

I'm creating a TryGetComponent(Type type, out Component component) method, and then adding a TryGetComponent<T>(out T component) overload for convenience:

public bool TryGetComponent(Type type, out Component component)
{
    component = null;
    if (type.IsAssignableFrom(typeof(Component)))
    {
        throw new ArgumentException($"Must get component using a type that is assignable to {nameof(Component)}: Parameter type was '{type}'", nameof(type));
    }
    return Components.TryGetValue(type, out component);
}

public bool TryGetComponent<T>(out T component) where T : Component
{
    return TryGetComponent(typeof(T), out component);
}

But I get the following compile error regarding ```out component`` in the second function.

Cannot convert from out T to out component

Adding an explicit cast to T gives the following error

The out parameter must be assigned before control leaves the current method.

I'm almost certain I've done this exact pattern before. No idea why it's not compiling.

Thomas Slade
  • 129
  • 9
  • 1
    Did you want a `!` in here: `if (type.IsAssignableFrom(typeof(Component)))`? – Retired Ninja May 30 '23 at 22:50
  • 2
    The issue here is that `T` could be a `Component` or it might be a type that derives from `Component`. So it is not a guarantee that `TryGetComponent` will return a suitable object to return as the out parameter. – John Wu May 30 '23 at 22:53

1 Answers1

2

As John Wu said in his comment, every T is a Component, but not every Component is a T.

It looks like you're trying to achieve the following:

  • One method to "TryGet" a component
  • A second method to "TryGet" a component of a particular derived type T, and either return false or throw if that component is not a T.

(whether you want to return false or throw is not clear because the implementation is not complete. You can modify my code to do whichever you prefer)

In your second method, you're trying to simply call the first method, but you need extra logic to check if that component is a T and than handle that.


Update: @Charlieface pointed out that the inner call specifies typeof(T) anyway, so we can assume the component is a T. But this can't be known at compile time, so we still need logic to cast it:

public bool TryGetComponent<T>(out T component) where T : Component
{
    component = null;

    var success = TryGetComponent(typeof(T), out var found);
    if (!success) {
        return false; // Component not found; return false
    }

    component = (T)t;
    return true; // Component found; return true
}

Old example:
So, something like:

public bool TryGetComponent<T>(out T component) where T : Component
{
    component = null;

    var success = TryGetComponent(typeof(T), out var found);
    if (!success) {
        return false; // Component not found; return false
    }

    if (found is not T t) {
        return false; // Component wrong type; return false or throw
    }

    component = t;
    return true; // Component found; return true
}
brads3290
  • 1,926
  • 1
  • 14
  • 20
  • Might as well do `var success = Components.TryGetValue(typeof(T), out var component);` directly. Also no real need to check the cast if the dictionary is always inserted with the correct type. – Charlieface May 30 '23 at 23:28
  • Oh yeah, I forgot that you're already specifying the `T` in the inner call - oops. I'll amend my answer; thanks! – brads3290 May 30 '23 at 23:31