- Home /
Why does ScriptableObject.CreateInstance, where T is definite at runtime, return null?
So I'm aware that for ScriptableObject.CreateInstance to work, 'T' can't remain generic.
I have the following code, in which 'T' is eventually made definite:
Abstract base class
public abstract class MouthSetGenericEditor<T> : Editor
{
public static void CreateMouthSet(T fillerResource)
{
var mouthset = ScriptableObjectUtility.CreateAsset<MouthSetGeneric<T>>();
}
}
Child class where type is made definite
[CustomEditor(typeof(MouthSetGeneric<Sprite>))]
public class MouthSetSpriteEditor : MouthSetGenericEditor<Sprite>
{
private static readonly Sprite placeholderSprite = Resources.Load<Sprite>("Placeholder");
[MenuItem("Assets/Rhubarb/Create Sprite Mouthset")]
public static void CreateSpriteMouthSet()
{
CreateMouthSet(placeholderSprite);
}
}
Utility where ScriptableObject.CreateInstance() is called
public static class ScriptableObjectUtility
{
public static T CreateAsset<T>() where T : ScriptableObject
{
T asset = ScriptableObject.CreateInstance<T>();
if (asset == null)
Debug.Log("asset null");
// more code...
}
}
In the same block where ScriptableObject.CreateInstance() is called, if I log typeof(T).ToString(), the console outputs MouthSetGeneric`1[UnityEngine.Sprite]. So it looks like type T was made definite, but the ScriptableObject instantiation call isn't behaving as though it is.
Any idea what could be going on? Thanks!
Answer by Bunny83 · Nov 15, 2018 at 01:29 PM
Ok first of all you should know that Unity can not serialize generic types, at all. They made an explicit / hardcoded exception for the generic List class but any other generic class can not be serialized. Therefore you can not have a generic MonoBehaviour or ScriptableObject instance. However what you can do is create a derived (non generic) class from your generic class for each type parameter you want to use and use those classes instead.
The second thing that seems a bit strange is that you call your method "CreateAsset" which you want to call at runtime. Assets in the Unity word means serialized objects in the project. Assets can only be created in the editor and not at runtime. However instances of objects or even of assets can be made at runtime (That's why the method is called CreateInstance).
It's not clear what you want to do here. If you want to create a clone / copy of an asset, just use Instantiate. You can instantiate scriptable object instances / assets just like any other object derived from UnityEngine.Object.
Finally while it is true that the generic type parameters are always bound at runtime, in normal C# the type parameters is actually fix at compile time based on the usage. C# doesn't have a special variable type for type parameters. Types are given in code. In your example you have defined a concrete class of your abstract editor class which you called "MouthSetSpriteEditor" and the generic type parameter is filled with the type Sprite.
While it is possible to actually bind the type parameter dynamically using reflection in order to generate a new type, however this is generally not the way to go and isn't supported at AOT platforms like iOS since it requires JIT compiling as you would create a new unknown type at runtime.
Finally keep in mind that when you use a generic class with two different types as T, those two generated concrete classes do not have anything in common in the sense of OOP. A List<int>
and a List<string>
are two seperate classes which happens to be created by the same generic class List<T>
but are completely seperate class types which can't interact in any way. It's like the difference between public class A {}
and public class B {}
. Even though .NET generics work completely different from C++ templates, the basic idea is the same. The generic class is not an abstraction in the OOP sense but more a generic template for different implementations.
Keep in mind we only see a fraction of the whole picture of your case. We don't know how this code is supposed to work and what the end result should look like. If you want to create some sort of factory, I'm afraight this isn't going to work since as i said at the top Unity doesn't support the serialization of generic types at all. At least up to this point in time ^^.
Thanks so much for all the info!
I should clarify none of the code is intended to be used at runtime--it's all for editor workflow.
Additionally, ScriptableObjectUtility
is a way to create ScriptableObject
asset files in the project without having to use [CreateAsset$$anonymous$$enu()]
--it comes from this article on the unify community. The reason I'm using it is because I can't specify a [CreateAsset$$anonymous$$enu]
attribute for the generated concrete classes that come from the generic ScriptableObject
$$anonymous$$outhSetGeneric<T>
.
$$anonymous$$y use case:
$$anonymous$$outhSetGeneric<T>
contains a `SerializableDictionary` with an explicitly typed set of keys, but a generic set of values. Why?
I have a given set of 'mouth shapes'. Those shapes map to a resource that visually represents that shape, for example of type Texture, Sprite, or $$anonymous$$esh.
The idea behind having a concretely-typed Editor class is that it can pass its given type to a newly created $$anonymous$$outhSetGeneric<T>
, so that the resource type its dictionary maps to can be specified by the user (e.g. as Sprite
).
Here's the code for $$anonymous$$outhSetGeneric<T>
:
public class $$anonymous$$outhSetGeneric<T> : ScriptableObject {
public SerializableDictionary<$$anonymous$$outhShape, T> shapeDictionary;
}
And here's the full code for the $$anonymous$$outhSetGenericEditor<T>
function, Create$$anonymous$$outhSet()
:
public static void Create$$anonymous$$outhSet(T fillerResource)
{
var mouthset = ScriptableObjectUtility.CreateAsset<$$anonymous$$outhSetGeneric<T>>();
mouthset.shapeDictionary = new SerializableDictionary<$$anonymous$$outhShape, T>()
{
{$$anonymous$$outhShape.A, fillerResource},
{$$anonymous$$outhShape.B, fillerResource},
{$$anonymous$$outhShape.C, fillerResource},
{$$anonymous$$outhShape.D, fillerResource},
{$$anonymous$$outhShape.E, fillerResource},
{$$anonymous$$outhShape.F, fillerResource},
{$$anonymous$$outhShape.G, fillerResource},
{$$anonymous$$outhShape.H, fillerResource},
{$$anonymous$$outhShape.X, fillerResource},
{$$anonymous$$outhShape.Error, fillerResource}
};
}
The desired outcome for all this is to be able to create a $$anonymous$$outhSetGeneric
asset in the project where both types in the asset's dictionary are specified, with the potential for the user to extend the types of resources that can be specified by creating new Editor classes. (I'm planning to create classes that support passing sprites and textures, but other users might want to pass something different, like a blendshape.)
Does that make any sense? Thanks so much for your time!!
Your answer
![](https://koobas.hobune.stream/wayback/20220612182150im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Getting a reference to a group of ScriptableObjects implemented at runtime? 0 Answers
PlayFab CloudScript NullReferenceException 0 Answers
Scriptable Object is getting set to null 0 Answers
Null Reference Exception For Blocks C# 1 Answer
Do I need to explicitly test for null or is testing the object itself good enough? 2 Answers