Cannot cast Object[] to Texture2D[] in c#?

I am trying to do this:

gallery_.galleryImages = (Texture2D) Resources.LoadAll(“Images/” + galleryAttributes.nameOfGalleries*, typeof(Texture2D));*_
galleryImages is an array itself, and it is initialized, and this gives me no compile errors
however when I run, I get this:
InvalidCastException: Cannot cast from source type to destination type.
Why can I not cast an Object Array to a Texture2D array?
This code does work when I remove the cast and make galleryImages into an Object[ ], so the error is all dependent just on the carting of Object[ ] to Texture2d[ ] in that single line
Does anyone know why this does this?

You forgot the brackets in your cast.

Oops, forgot to put the brackets in the example code, I did originally have brackets in the code that was giving me the error

gallery_.galleryImages = (Texture2D[ ]) Resources.LoadAll(“Images/” + galleryAttributes.nameOfGalleries*, typeof(Texture2D));*_
That still gives me the error
InvalidCastException: Cannot cast from source type to destination type.
When using Object[ ] it works fine and I can later cast all the calls to .galleryImages to Texture2D just fine. Is there something that specifically inhibits you from casting the Resources.LoadAll function?

No problems here with generic variables; it must be some problem with your variable types not matching.

Texture2D[] textures = (Texture2D[]) Resources.LoadAll("Images/", typeof(Texture2D));

An Object array (Object[ ]) is not the same as Texture2D array (Texture2D[ ]).

Even if all the elements in the Object array are of type Texture2D, you cannot simply cast it by doing (Texture2D[ ])ArrayOfObjects.

Resources.LoadAll explicitly returns an Object[ ]. You must declare a Texture2D[…] and then copy over the references. This is NOT a C# generic function.

No, it really works fine. Try it.

[SerializeField] GameObject[] gameObjects;

void Reset () {	
	gameObjects = (GameObject[]) FindObjectsOfType(typeof(GameObject));
}

Read this for an explanation:
http://forum.unity3d.com/threads/40114-C-List-vs.-JavaScript-Array?p=259592&viewfull=1#post259592

Personally, I’d do

gameObjects = this.FindObjectsOfType<GameObject>();

using extension methods:

public static T FindObjectOfType <T> (this Object unityObject) where T : Object {
	return Object.FindObjectOfType(typeof(T)) as T;
}
public static T[] FindObjectsOfType <T> (this Object unityObject) where T : Object {
	return Object.FindObjectsOfType(typeof(T)) as T[];
}

because I hate having to deal with that extra clutter. I wish they’d put it in the API, so this. wasn’t necessary, though.

Hmm. I guess unity does initialize the right type of array.

I actually just load all the resources at once, separate them out into a dictionaries by type, and then make a generalized function

Fetch(name)

and use that throughout my code.

Sorry to dig up this old post but this doesn’t work correct?

Since we are trying to cast Object[ ] to a Texture2D[ ] … casting to a lower level than what the function (Resources.loadall) returns doesn’t work… ?

Are you asking why or how?

How:

Texture2D[] textures = System.Array.ConvertAll(Resources.LoadAll("folder", typeof(Texture2D)),o=>(Texture2D)o);

Slower but I <3 linq :slight_smile:

using System.Linq;
Texture2D[] textures = (Resources.LoadAll("folder", typeof(Texture2D))).Cast<Texture2D>().ToArray();

Why:

Try googling ‘array contravariance’

Getting confirmation since this doesn’t work for me but I keep seeing examples of what Jessy posted and keep wondering if I am not missing something :slight_smile:

FindObjectsOfType is annotated with TypeInferenceRule an internal Unity attribute which looks to enable the casting based on rules defined in the attribute (in this case the type of the first argument).

EXPLANATION:

http://answers.unity3d.com/answers/733688/view.html

Who cares - there’s a generic overload of LoadAll now :slight_smile:

It’s been 6 years, but for anyone looking for a final answer, in the 2020.3.19f1 version of unity, the following does not work:

You have to cast them one by one in a loop like this:

Object[] texObjs = Resources.LoadAll("Path/To/Textures");
Texture2D[] textures = new Texture2D[texObjs.Length];
for (int i = 0; i < texObjs.Length; i++)
    textures[i] = (Texture2D) texObjs[i];

or if you don’t want to initialize a new array during runtime or just want your code to be more readable you can use a List:

// Don't forget to include "using System.Collections.Generic;" at the top
List<Texture2D> textures = new List<Texture2D>();
Object[] texObjs = Resources.LoadAll("Path/To/Textures");
foreach (Object obj in texObjs)
    textures.Add((Texture2D) obj);

Btw I have not tried out casting using “(Texture2D) obj” so if that doesn’t work try “obj **as** **Texture2D**” instead

Does the example in the doc not work? Because it certainly seems simpler than all that.

// Loads all assets in the "Resources/Textures" folder
// using Linq.
using UnityEngine;
using System.Linq;

public class ExampleClass : MonoBehaviour
{
   void Start()
   {
       var textures = Resources.LoadAll("Textures", typeof(Texture2D)).Cast<Texture2D>().ToArray();
       foreach (var t in textures)
           Debug.Log(t.name);
   }
}
1 Like

Uhm, have you actually read the last two posts above yours? There is now a generic version of LoadAll. It actually returns an array of the type you pass in.

Texture2D[] texs = Resources.LoadAll<Texture2D>("Path/To/Textures");

Also in my UA answer that Fattie linked above I did suggest either manually casting each element manually, or use linq to do it. That was 8 years ago. I don’t remember when the generic version got added, but judging by KelsoMRK’s post it’s there for at least 6 years.

Since covariance and contravariance was mentioned earlier in this thread, I want to make something clear here. Usually arrays of different types can not be casted into each other, even when one of the element types is derived from the element type of the other. However C# does allow only covariance for arrays. This is essentially a special case which can lead to runtime errors when not handled correctly. Note that contravariance is not supported at all for arrays. So you can not create an Object[ ] and cast it to a Texture2D[ ]. That’s just not allowed. The issue here is that when putting elements into an array a type check is performed by the CLR. So in the covariance case (treating a Texture2D array as an Object array), the CLR will check the object type against the actual element type and throw a runtime exception if you try to put anything other than a Texture2D into that object array.

However for the contravariance case, the actual Object array could already contain non Texture2D objects. So casting it to a Texture2D array is not allowed as this can not be ensured. The CLR would have to iterate through all elements in order to allow such a conversion. Even if such a check was performed, you could use the object array reference to exchange an element of the array with a non Texture2D element and you’re back at the start. The issue is that reading an array element does not perform a type check. That would be expensive in all usual cases. So if you have a Texture2D array, you can be sure it only contains Texture2D elements. That’s why contravariance is not supported for arrays.

Even covariance is an issue, but at least if misused the CLR can detect misuse and throw an exception.

Texture2D[] texArr = new Texture2D[5];
Object[] objArr = texArr; // array covariance allows this
objArr[0] = new Mesh(); // this does compile but it would throw an exception at runtime

co- / contra- variance (warning)

The array covariance support is a very special case and only native .NET arrays support this. This does not work with generic Lists. It’s an explicit language feature that was added very early on due to some design requirements. So this is a very special case and not directly related to the actual feature of covariance / contravariance that the language supports.

Co and contra variance are usually one-way features and you can only have one or the other for a certain type. It’s actually about generic arguments.

Covariance
is about generic type variance that flows out the class / type. For it to work, the generic argument has to be decorated with the out keyword. Such generic type parameters can only be used in the class for outflowing data. So only return types, out parameters or readonly properties can use that type. This ensures there’s never a case where data of that type has to flow into the class. Covariance allows you to treat a generic class with a certain type argument as the same generic class with a base type of that argument. Here’s a more contrete example

public interface IGetSomething<out T>
{
    T Get();
}

public class MyStringReturningClass : IGetSomething<string>
{
    public string Get()
    {
        return "Hello world";
    }
}

IGetSomething<object> getAnObject = new MyStringReturningClass();

object o = getAnObject.Get();

As you can see, the interface we defined here has a generic type argument, restricted as out parameter. We use it in the interface as a return type. So data flows out of the class. We can successfully cast our “MyStringReturningClass” to an IGetSomething<object> since the type “object” is a base class of “string”. So we get less specific so the cast is possible. However it would not be possible the other way round. So having a class implementing IGetSomething<object> and then try to cast an instance of such a class to a IGetSomething<string> is not allowed, since the actual class could return any kind of object, not just a string. However the casted reference would need to return a string. So that’s not possible.

Contravariance
on the other side is about data flowing into the type. Generic arguments would need to be decorated with the in keyword to restrict the type usage to only be used for parameters of methods. Here the whole dataflow is reversed.

public interface IDoSomething<in T>
{
    void Do(T data);
}

public class MyObjectReceivingClass : IDoSomething<object>
{
    public void Do(object data)
    {
        // do something with data;
    }
}

IDoSomething<string> someActionWithString = new MyObjectReceivingClass();

someActionWithString.Do("Hello World");

As you can see, here the whole argument goes the other way round. Here the actual implementation has to be the more generic version, accepting an argument of type object, and we can cast our instance to a more restrictive interface type that only allows strings. That’s because the data flows into the class. So restricting the incoming type to a more concrete type does not impose any problem since the actual class expects an object and string is an object.

Here again, the other way round would not work. You can not have a class that expects a string argument and cast it to an interface that expects an object.

Conclusion
Covariance and contravariance only ever work in seperation from one another. You can not have a generic type argument that allows both in and out (which is the default if nothing is specified) and have co or contra variance working. The best example is the generic List class. It has methods that accepts elements (Add, Insert, …) and it has methods that return elements (this[ ]). So if you would allow to cast a List<string> to an IList<object> for example, we could use Add(new object()) on the IList and it would violate the contravariance restrictions. Likewise if you reverse the case, having a List<object> and cast it to a IList<string> would violate the covariance restrictions since an IList<string> should only return strings which can not be ensured.

That’s why co / contra variance is only supported in one-way scenarios. Of course you could implement two additional interfaces, one that only provides the reading aspects and one only providing the writing aspects of IList and have them be co / contra variance compatible. Though you can not make the System.Collections.Generic.List class implement those. Though you can do that with your own derived class(es).

Well, it only seems like it’s simpler :slight_smile: The linq extension method Cast<Tector2D> would actually do the same and iterating through all elements and casting each element to the target type. The ToArray extension method would then accumulate all elements in an internal buffer / list and construct a new array our of the elements. So using linq certainly looks shorter, but in most cases is more expensive and slower compared to other solutions. As it was already mentioned, we do have a generic version of LoadAll which should be used anyways and does not have this issue.

Fair - but when loading something off disk like this I think a couple of extension methods are the least of your worries when it comes to performance.

Even though I was the one who said it, I couldn’t remember if there actually was a generic overload for this and the docs don’t actually mention it. :slight_smile:

With all that said, even the generic overload is basically doing the same thing so at the end of the day I’d argue it’s more about readability than anything.

internal static T[] ConvertObjects<T>(Object[] rawObjects) where T : Object
{
  if (rawObjects == null) return null;
  T[] typedObjects = new T[rawObjects.Length];
  for (int i = 0; i < typedObjects.Length; i++)
    typedObjects[i] = (T)rawObjects[i];
  return typedObjects;
}
public static T[] LoadAll<T>(string path) where T : Object
{
  return ConvertObjects<T>(LoadAll(path, typeof(T)));
}

https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Resources/Resources.bindings.cs

Uhm, it does, just scroll down a bit ^^.

Well, yes, the outcome is the same, but the overhead is not, especially with a large number of elements. You’re right that the generic version also does the manual implace conversion into a new array, however the linq solution allocates more memory. That’s because the ToArray extension method can not determine how many elements are in the IEnumerable. So it uses a Buffer internally that works similar to a List. So it starts with a small array and as you add elements, the array is replaced by a larger one and the old data is copied over. Sure, for a small number of elements it wouldn’t really matter. However the generic version is already build-in, is cleaner and shorter to write and you can’t really get a better performance unless you would directly work with the IEnumerable so you can avoid the extra array allocation.