Can cast to interface, but not to generic type

541 Views Asked by At

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.

1

There are 1 best solutions below

1
On BEST ANSWER

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:

public class SerializableInterfaceReferenceForUnity<TSomeInterface> 
       where TSomeInterface : class
{
    public TSomeInterface obj
    {
        get
        {
            if (objIsUnityType) { return unityObj as TSomeInterface; } 
            else { return rawObj; }
        }
}

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 your SerializableInterfaceReferenceForUnity class.