I’m using Odin to create custom UI in the inspector to facilitate attaching assets to the main game object of the prefab. I’ve accomplished my goal but after writing nearly 1000 lines of code and a lot of it is repetitive. So I thought “hmm, I should refactor this in a way that will remove the repetition”. So I have 4 “drawCell” methods for a table grid (not important) and three of them operate on Texture2Ds and the fourth operates on a custom class that has two fields: a char and a Texture2D. Originally I wanted to create a single method and then have all the draw methods call that method but it got too cumbersome so I changed it to be a class where I could set certain properties and then call the object’s draw method. This would solve the problem and it would work for anything. I wrote the class, and made it generic since I have two types to work with (for now, I might use more types later) but here’s the problem: I need to limit the T of the generic to be either a Unity object which means the inspector can instantiate it, or one of my classes that I’m basically overwriting how it will show in the inspector. So I figured, "Ok, I’ll make it so that the class is “where T : UnityEngine.Object”.
Here’s the bug: UnityEngine.Object cannot be derived from without breaking everything in the class. Trying to do anything with it causes a NullReferenceException even though it’s been instantiated with new. Additionally, all UnityEngine Objects can be “not null” and yet still throw NullReferenceException when the object is used. Why? because the object is still “null” even though according to C# it’s not null. The documentation even says that you can’t use the ?. and ?? operators on it. I don’t even really care to use anything to do with UnityEngine.Object. I just want the type system to say “yup, that can be specified as T for this class too”.
But I can’t. So I’m stuck with 110 lines of code that can be reduced to far less or would be able to be reduced if UnityEngine.Object didn’t completely break C#. Why? Who’s Paula Bean level brillant idea was it to do this? Please fix this! It’s totally broken.
Your poignant observations and tilting-at-windmills are duly noted.
However, rivers of digital ink have been spilled about the null equality operator overload in Unity, so read around a bit and you will see that the odds of this feature getting changed anytime soon are pretty small.
The reason: it would break probably half of the code out there.
I could even live with an empty interface that all Unity Objects “implement”. Then at least I can attach that interface to my classes, and limit to Types that implement that interface. That would at least solve this specific case.
And what do you mean “would break half the code out there”? Seriously, please describe how making it so that ctoring a UnityEngine.Object doesn’t instantly throw NRE would break code. It’s something people aren’t doing since they literally aren’t able to because it throws NRE.
You got the wrong idea in your head. A UnityEngine.Object derived type is a very special kind of object. They ALWAYS have a native C++ counterpart. That’s the purpose of the UnityEngine.Object class, nothing more. Since your class does not have this link to a native C++ class (because you created it with new) you already violated the single one purpose that this class has. You try to use a class for something it wasn’t made for and complain why the square doesn’t fit your round hole ^^.
This is literally the source code for the UnityEngine.Object class. When you derive your own class from a base class you inheriting the purpose, the fields and the functionality of that base class. If you have a class X derived from class B the following statement is always true: An instance of type X is an instance of type B. If you can not say that about your class you should not derive it from B.
I don’t know Odin and don’t quite understand what you’re trying to do, but the usual Unity-ish way is to inherit from a script component. class myDrawBase : Monobehaviour then class myDraw1 : myDrawBase.
And then C#'s generic system is most of the problem. It’s not good. In C++, which C# got it from, you don’t need a where. Generics there work with any class they can, with a compile-time error when they can’t.
Well C# generics and C++ templates only share the same syntax but have nothing else in common. In C++ templates are literally just a compile time search and replace syntax, nothing more. This simplicity makes it much more powerful but it’s a pure compiler feature. C# generics are a runtime feature. Generic classes can only take type parameters and are compiled without knowing the concrete type. So all the generated code has to work equally with any type allowed. You don’t need a where in C# either, only when you want to access something from the type provided through the type parameter.
Note that in C++ you can also specify other parameters through template arguments. I once made a general purpose template matrix class in C++. It had 3 parameters. One for the element type and the other two specified the column and row count. Since classes and structs are exactly the same in C++ (only the default field visibility is different) you could easily create an instance on the stack with any dimension size. So the column and row count was a compile time configuration rather than a dynamical parameter to the constructor.
I haven’t used Odin yet. I’ve heard that it can somehow serialize things that are normally not supported by Unity. Though I don’t know how they achieved that. Maybe through some hacks on top of Unity’s serialiation system or by rolling their own in some way. Anyways this does not change the purpose of the UnityEngine.Object class ^^. The UnityEngine.Object class can not be ever used as a base class for your own types. The only two classes you can extend are MonoBehaviour and ScriptableObject which are the same thing on the native C++ side. Of course MonoBehaviours have to be attachd to a game object while scriptable objects need to be created with the CreateInstance method.
Though I’m also not sure what the OP actually wants and where the exact issue is. The only issue at hand is the misuse of the UnityEngine.Object class. Without more information we can’t really help any further here.
This all makes no sense at all. I think you also did not understand the point of the UnityEngine.Object class. It is itself a wrapper for a native C++ object. However creating the wrapper object with new results in an empty wrapper object that is unusable from the engine’s point of view. A UnityEngine.Object that has lost its native counterpart will fake that it’s null. Again you are not supposed to derive any class from UnityEngine.Object. It’s just the common base class of Unity’s own built-in wrapper classes.
Also I don’t get the point of your code example. Your interface has a generic type parameter that isn’t used for anything. Also what’s the point of the Ref1 property?
So…You decided to make your classes inherit from some library class purely so that you could write a single constraint that would match both of them? This seemed like a good idea?
And now that that went wrong, you think the library is the one that ought to change? (So that your already working code can be made shorter?)
I suspect you may have an acute case of tunnel vision. You had an idea that seemed like it would work, and then when you discovered a problem, you decided that it must be the rest of the world that is at fault, rather than there being something wrong with your original idea.
As a rule of thumb, it’s usually a bad idea to become attached to implementation details. There’s probably several other ways you could accomplish your basic goal. You could write your own interface, then a wrapper class that maps UnityEngine.Object to that interface. Or instead of your draw-manager-thing using a constraint that somehow matches everything you care about, it could have multiple subclasses for handling different related types. Or you could remove the constraint and do something with the “is” or “as” keywords to trigger different code branches depending on the dynamic type of your data. Or you could just create two separate collections or functions or whatever-it-is that this type parameter corresponds to.
Or maybe you couldn’t do those specific things. I haven’t seen your code. But odds are high that you have some options other than “hope that Unity implements a new, unspecified interface on the most fundamental type of their entire engine just for you” or “give up on all refactoring forever”.
The ref1 property was a link to the unityengine object the op wanted to use. It was in the interface and the class had to follow rules. Not sure what to call anything since no code was ever posted.
There probably is no good reason to derive from unityengine object class, but I am going to at least indulge the idea someone could have a use for it.