Tuesday, March 1, 2011

[C#] cannot convert from 'out T' to 'out Component'

Hello,

here is some C# code that does not compile, giving the following message :

cannot convert from 'out T' to 'out Component'

    public void Get<T>(out T c) where T : Component
    {
        m_components.TryGetValue(typeof(T), out c);
    }

Here is code that does compile :

    public void Get<T>(out T c) where T : Component
    {
        Component temp;
        m_components.TryGetValue(typeof(T), out temp);
        c = (T)temp;
    }

I wonder why the first code is not valid since the "where T : Component" explicitly states that T is of type Component.

Thanks

From stackoverflow
  • I think the problem is in the out and it does not relate to Generics

    we can produce the same error as follows

    class A
    {
    }
    
    void fn(out object x)
    {
     x= new object();
    }
    void main()
    {
     A x;
    fn(out x); //error
    }
    
  • This is interesting.

    I don't have an answer, but it's worth noting (to me, anyway), that the following works:

     public void M<T> (out T c) where T : Test
     {
      // Test m;
      // A(out m);
      // c = (T) m;
    
      A(out c);
     }
    
     public void A<T> (out T t) where T : Test
     {
      t = null;
     }
    

    -- Edit:

    (It's interesting because even if it's out object t it's still not convertible between out T and out object)

  • It's because out parameter types cannot be covariant/contravariant. The type of the variable must exactly match the parameter type.

    See:

    class Super { }
    class Sub : Super { }
    
    void Test(out Super s)
    {
        s = new Super();
    }
    
    void Main()
    {
        Sub mySub = new Sub();
        Test(out mySub); //doesn't work
    }
    
    FlySwat : you beat me to it :(
    Chris Ballance : Nice explanation and example
  • OK, I've figured it out:

    What if you have the following:

     public class BaseClass { ... }
     public class SubClass : BaseClass { ... }
    

    then I have the code:

     Dictionary<int, BaseClass> comps;
    
     public void Get<T>(int num, out T c) where T : BaseClass
     {
         comps.TryGetValue(num, out c);
     }
    

    And I try to call it as thus:

     SubClass sub;
     Get<SubClass>(1, out sub);
    

    Sub assume that the BaseClass with a key of 1 was actually a BaseClass and not a SubClass. Or Perhaps it is OtherSubClass where OtherSubClass : BaseClass

  • I would guess that TryGetValue looks like :

    bool TryGetValue (Type someType, out Object result){ 
       // do stuff//
    }
    

    thus result is an object rather than component and cannot be implicitly converted, which is what you are trying to do.

  • I'm going to take a shot at a more elaborate version of @Rex's answer and slightly corrected version of @Courtney's signature in that the type is Component, not object. Courtney's answer is basically right, just the types are a bit off.

    bool TryGetValue(Type key, out Component result)
    {
           if (this.Contains(key))
           {
               result = this[key]; // the type of result is Component!
               return true;
           }
    
          return false;
    }
    

    by passing T as the object type, you are trying to implicitly cast the base type Component to a subtype T. That is why your second example works. TryGetValue doesn't know about your Generic type T, it thinks everything in m_Components is a Component object.

    This is a pretty common thing to get stuck thinking backwards. Because it's a parameter, not the return type, you get sucked into thinking it should work like any other parameter. However, because it's an out, it actually is better thought of as a return type for this purpose... it is going to try to assign the value of it's internal work to the parameter you provide.

0 comments:

Post a Comment