TL;DR: 1) A non-generic version of my code allows me to cast an object to an interface that I know it implements; a generic version, where I'm casting to a generic type, will not compile. Am I missing a constraint and/or a workaround? 2) If C# doesn't support what I'm doing, is there another way to accomplish my broader goal?
The closest I've come to finding a similar problem to my own is this, where the only answer was "This makes no sense since it doesn't leads [sic] to any limitations." So, let me spell out the exact limitation I'm facing...
Here is the non-generic version of my code; hopefully the inline comments will explain what I'm trying to do, and why:
public interface ISomeInterface
{
string SomeFxn();
}
[Serializable]
public class SerializableInterfaceReferenceForUnity
{
// Unity has long been able to serialize references to its own objects, but using this
// attribute on a "raw C#" object (i.e., anything other than Object) would try to
// serialize the **value**.
[SerializeField] private UnityEngine.Object unityObj;
// This new(-ish) attribute actually allows us to serialize references ... but ONLY to
// "raw C#" objects! (If rawObj is a UnityEngine.Object that implements
// ISomeInterface, I get error messages in the console.)
[SerializeReference] private ISomeInterface rawObj;
// So, a kludge: branching my logic based on the type that implements ISomeInterface.
[SerializeField] private bool objIsUnityType;
// In my defense, I *think* the kludge will be entirely hidden on the back-end;
// the API will "just work":
public ISomeInterface obj
{
get
{
// FLAG: this next line works here, but breaks in generic version
if (objIsUnityType) { return (ISomeInterface) unityObj; }
else { return rawObj; }
}
set
{
switch (value)
{
case UnityEngine.Object uo:
{
objIsUnityType = true;
unityObj = uo;
rawObj = null;
return;
}
default:
{
objIsUnityType = false;
rawObj = value;
unityObj = null;
return;
}
}
}
}
}
The above is all well and good, but the real goal is to allow me to serialize ANY interface in Unity. So, here's a generic version ... which is line-for line the same, except ISomeInterface
becomes TSomeInterface
:
[Serializable]
public class SerializableInterfaceReferenceForUnity<TSomeInterface>
// where TSomeInterface : { what goes here?! }
{
[SerializeField] private UnityEngine.Object unityObj;
[SerializeReference] private TSomeInterface rawObj;
[SerializeField] private bool objIsUnityType;
public TSomeInterface obj
{
get
{
//"CS0030 Cannot covert type 'UnityEngine.Object'
// to 'TSomeInterface'" on the below:
if (objIsUnityType) { return (TSomeInterface) unityObj; }
else { return rawObj; }
}
set
{
switch (value)
{
case UnityEngine.Object uo:
{
objIsUnityType = true;
unityObj = uo;
rawObj = default;
return;
}
default:
{
objIsUnityType = false;
rawObj = value;
unityObj = null;
return;
}
}
}
}
}
I'm crossing my fingers that all I need is a constraint on the generic type.
Barring that, is there any kind of workaround (say, some reflection magic) to change an arbitrary object to a (generic) interface type?
Barring THAT, is there any way to accomplish the same objective (namely, Unity serialization of references to objects that implement a given interface, regardless of whether those objects are Unity built-ins or raw C#) in a clean way?
Feedback / rotten tomatoes regarding my kludge are also welcome.
I don't know anything about Unity, but assuming that
UnityEngine.Object
is a reference type, then you should be able to do something like this:The only thing to keep in mind is that you will not be able to use any value type (
struct
) as the generic type for yourSerializableInterfaceReferenceForUnity
class.