1
class Building {
}

class Home: Building {
}

private List<Building>   buildings;
public T[] FindBuildings<T>() {       
    return buildings.FindAll(x => x is T).ToArray() as T[];
}

I have a list to save all the buildings on the map, and write a function to find the certain building type. I want using FindBuildings<Home>() to get an array that contains all home buildings on the map, but when I run the code, I found the function always return null, it seems as did not work. Does this mean as can not using generic type parameter?

So, I change the code:

private List<Building>   buildings;
public Building[] FindBuildings<T>() {       
    return buildings.FindAll(x => x is T).ToArray();
}

using FindBuildings<Home>() as Home[] to access the home buildings on the map. But...it still get null.

I saw a Unity3d's example, show me the using 'as' to convert array type, why I failed?

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
    void OnMouseDown() {
        HingeJoint[] hinges = FindObjectsOfType(typeof(HingeJoint)) as HingeJoint[];
        foreach (HingeJoint hinge in hinges) {
            hinge.useSpring = false;
        }
    }
}
jiejieup
  • 11
  • 3
  • You can't cast a `Bulding[]` to a `House[]`, even if you know that each entry of the source array is a `House`. – Dirk May 20 '13 at 07:59
  • For the title: [You can't cast base type object to derived type](http://stackoverflow.com/questions/124336/a-way-of-casting-a-base-type-to-a-derived-type) – Habib May 20 '13 at 07:59
  • @Dirk dammit, never mind- I got the cast direction backwards - my bad – Marc Gravell May 20 '13 at 08:02

2 Answers2

10

You need to do it like this:

return buildings.OfType<T>.ToArray();

Explanation of why your attempt did not work follows.

The type of the expression buildings.FindAll(x => x is T) is List<Building>. This is because buildings itself is a List<Building>, and FindAll does not magically change the type of the item in the return value. Therefore, ToArray() produces a Building[].

The as operator returns a non-null value only in those cases where a straight cast would be valid. Since a cast from Building[] to Home[] is not valid, as always produces null. Don't be confused by the fact that casting a Home[] to a Building[] is valid.

Jon
  • 428,835
  • 81
  • 738
  • 806
7

The problem is that a Building[] isn't a Home[]. So this code:

public T[] FindBuildings<T>() {       
    return buildings.FindAll(x => x is T).ToArray() as T[];
}

... always tries to treat a Building[] as a T[], and will return T[] when T is neither Building nor Object. You need to create a T[] to start with. Fortunately, LINQ actually makes this very easy for you:

public T[] FindBuildings<T>() {       
    return buildings.OfType<T>().ToArray();
}

This important thing is that OfType<T> returns an IEnumerable<T> - whereas your call to List<Building>.FindAll is still returning a List<Building>, so ToArray is creating a Building[].

As an aside, I'd thoroughly recommend using casts instead of as unless it's valid for as to return null. If you want a casting failure to be an error condition, a straight cast indicates that via an exception immediately, rather than you getting a NullReferenceException later on, masking the original problem.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194