Closed will-aristocrat closed 1 year ago
Thanks for reporting the issue.
I guess you mean that the implementation of UnwrapAs<T>()
for example does not use the as
keyword to convert to the generic type provided. I am not sure that this is a problem at all though because if the method does return a valid object it will be guarenteed to be implicitly convertable to the generic type provided, so it would appear that the as
in this case would just do nothing.
For example given the following interpreted class declaration:
public class Example : MonoBehaviour{}
Lets assume that we have created a CLRInstance and want to unwrap it to UnityEngine.Component
using UnwrapAs<>. The following code using the default unwrap implementation will output the message commented:
Debug.Log(inst.UnwrapAs<Component>());
// Output: 'GameObject (UnityEngine.MonoBehaviourProxy)'
Modifying the UnwrapAs<>
implementation to also include the conversion using the as
keyword as shown below will output the same result because MonoBehaviourProxy
is the actual object type and not the variable type used to hold it.
Modified UnwrapAs implementation:
public static object UnwrapAs<T>(this object instance) where T : class
{
// Check for valid clr instance
if (instance != null && instance is CLRInstance)
{
// 'as T' was added
return ((CLRInstance)instance).UnwrapAs(typeof(T)) as T;
}
return instance;
}
// Output: 'GameObject (UnityEngine.MonoBehaviourProxy)'
Maybe I have misunderstood the issue but using the as keyword would have no effect in this case? Perhaps you mean that the UnwrapAs<>
method should return T
instead of object
in this case? That change can be made if you think it would be beneficial and the as
keyword would be required in that case. The non-generic version though would have to remain with object
as the return type for obvious reasons.
Yes that is one aspect that I think would be good to alter w/ the generic type being the return type as well, however the issue I was trying to explain is slightly different.
Given the runtime class public class Example : MonoBehaviour{}
, and having created an instance at runtime, if I then wanted to retrieve information about the class' fields at runtime via reflection...
var instance = monoBehaviourProxy.Instance.UnwrapAs<Component>() as Component;
var componentType = instance.Type;
var visibleFields = componentType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
It will return the proxy object's fields and not the instance's, because MonoBehaviourProxy is also a Component. Any reflection that I attempt to perform on what I would've thought would be the instance will actually be done on the proxy and not reveal the correct fields.
https://github.com/scottyboy805/dotnow-interpreter/blob/master/Assets/dotnow/Scripts/Core/CLRInstance.cs#L156 Line 156 is where it hits in this use case, which returns the .baseProxy of the CLRInstance. I would've thought it would return the instance itself.
The same would result if I was attempting to unwrap the exact type of the instance up on line 139. I may be misunderstanding how the proxies/instances are meant or able to be used though.
Thanks for explaining.
In that case the Unwrap call should not be needed at all since you can just use monoBehaviourProxy.Instance.Type
. The unwrap call in this case may be actually causing the issue because its intended purpose is to access the instance as an interop type that can be passed to external calls in different assemblies. For example: if you wanted to destroy the component from non-interpreted code then you would use something like Destroy(instance.UnwrapAs<UnityEngine.Object>());
I did a quick test with the some similar reflection code as shown above and got back the expected field: Interpreted:
public class Interpreted : MonoBehaviour
{
public int myfield;
}
Reflect:
MonoBehaviourProxy proxy = ...
Type componentType = proxy.Instance.Type;
FieldInfo[] fields = componenetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
// Output: fields = 'FieldInfo[1]', fields[0] = 'System.Int32 myfield'
Maybe I still don't have the full picture or the unwrap call is confusing matters, but if you are still not getting correct results then a full example would be great.
Also it is very WIP but there is a custom editor for mono behaviour proxies which aims to draw all fields on an interpreted class in the inspector. Might be worth checking out as it seems similar to what you want to acheive: https://github.com/scottyboy805/dotnow-interpreter/blob/master/Assets/dotnow/Scripts/Editor/MonoBehaviourProxyEditor.cs
I think this was a misunderstanding on my part of the intended usage for the unwrap call! Your reflection example is similar to a workaround I tested that also worked so I think I was just confused. Thanks for the editor script reference, this was actually something else we were hoping to accomplish :)
When using MonoBehaviourProxy/CLRInstance unwrapping, dotnow checks for compatibility against base type and returns the baseProxy if it matches. This causes issues where attempting to unwrap an instance with certain types (e.g. Component) ends up returning the proxy in its original form instead of casting the runtime instance.
pseudo repro steps: proxy.UnwrapAs()
proxy.instance.UnwrapAs()
returns proxy instead of type cast instance