-1

Why is it that I can cast a Component class instance to an interface like:

public Component FindComponent<T>()
{
    var comp = (from c in components
                where typeof(T).IsAssignableFrom(c.GetType())
                select c).FirstOrDefault();

    return comp;
}

foreach(var a in actors)
{
    IHealth health = a.FindComponent<IHealth>() as IHealth;
}

But I can't cast it inside the FindComponent() if I do:

public T FindComponent<T>()
{
    var comp = (from c in components
                where typeof(T).IsAssignableFrom(c.GetType())
                select c).FirstOrDefault();

    return comp as T;
}

As this gives the error: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint.

It's just easier for the user to not have to cast after the FindComponent(). Unity 3D has a similar function and you don't have to cast after it comes back and I'm not sure how it does the cast inside FindComponent() but I'm trying to replicate that.

Servy
  • 202,030
  • 26
  • 332
  • 449
user441521
  • 6,942
  • 23
  • 88
  • 160
  • add `where T : class` to the function: `public T FindComponent() where T : class` – Fabio Sep 06 '17 at 17:57
  • Didn't change anything. Same error. – user441521 Sep 06 '17 at 17:58
  • Side note: you can simplify your first linq query by doing this: `var comp = components.FirstOrDefault(c => typeof(T).IsAssignableFrom(c.GetType())` – Aleks Andreev Sep 06 '17 at 18:00
  • @AleksAndreev I prefer the sql like syntax personally. – user441521 Sep 06 '17 at 18:01
  • @user441521, `where T : class` will remove compiler error you mentioned. (as error message stays itself _because it does not have a class type constraint nor a 'class' constraint_. – Fabio Sep 06 '17 at 18:03
  • Yes, but now it's erroring out saying it cannot convert type 'Component' to 'T'. So Health is a class that derives from Component and implements IHealth. The idea is querying the components list, which I added a Health object to, to find a component that implements IHealth. It finds the Health object correctly, but I'd want to only return the IHealth component so I can only interact with the interface methods. – user441521 Sep 06 '17 at 18:06
  • 3
    Your method can simply look like: `return components.OfType().FirstOrDefault();` – Fabio Sep 06 '17 at 18:08
  • Yes, what Fabio mentioned is better - the casting occurs in "OfType" and you don't have to worry about it. Then you just return the first object from that function. – Kevin Hirst Sep 06 '17 at 18:09
  • That works Fabio thanks! Strange the description in the editor of that function doesn't mention that it casts to that type. – user441521 Sep 06 '17 at 18:10
  • @Fabio `OfType` is for sequences that may not be of the proper type. `Cast` is for casting objects you know are of another type. – Servy Sep 06 '17 at 18:17
  • @Servy, as OP said in the comments _to find a component that implements IHealth_ - I think it's fit to the _sequences that may not be of the proper type(IHealth in OP case)_ – Fabio Sep 06 '17 at 18:20

1 Answers1

0

The 'as' keyword will give you a null value if the cast fails. But in this case, what you need to do is an implicit cast, because you've already checked that this is possible.

return (T) comp;
Kevin Hirst
  • 876
  • 8
  • 17
  • This gives me the error: Cannot convert type 'Component' to 'T'. Cast is redudant but if I remove the cast I get error: Cannot implicitly convert type 'Component' to 'T'. – user441521 Sep 06 '17 at 17:56
  • Ok. Does the "components" queryable/list/whatever implement an OfType<> method? – Kevin Hirst Sep 06 '17 at 18:08