Buildingblocks .NET conversie 2018-06-14 14:54

UWP projecten kunnen op 2 manieren gecompiled worden in Unity:

Om gebruik te maken van Vuforia moet het project gecompiled worden met de .NET scripting backend. Dit zorgde ervoor dat een heleboel code van de buildingblocks kapot ging. Het grootste probleem is dat veel properties en methodes uit de 'Type' klasse zijn verplaatst naar een nieuwe 'TypeInfo' klasse. Er is daarom een 'TypeUtil' klasse gemaakt die dit probleem oplost zodat code voor zowel UWP als niet-UWP platforms werkt:


using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;

#if NETFX_CORE
using System.Threading.Tasks;
using System.Linq;
#endif

namespace BlueTea.BuildingBlocks.General.Extensions
{
    /// <summary>
    /// Utility class for differences in the reflection API of mono and .NET for UWP
    /// </summary>
    public static class TypeUtil
    {
        #region Public

        /// <summary>
        /// Returns the correct instance of <see cref="TypeInfo"></see> (System.Reflection.TypeInfo for NETFX_CORE and the
        /// dummy class for other platforms)
        /// </summary>
        /// <param name="type">The type for which to get the typeinfo
        /// <returns>The correct TypeInfo instance</returns>
        public static TypeInfo GetTypeInfoSafe(this Type type)
        {
#if NETFX_CORE
            return type.GetTypeInfo();
#else
            return new TypeInfo(type);
#endif
        }

        /// <summary>
        /// Gets the constructor of a type with no parameters
        /// </summary>
        /// <param name="type">The type for which to get the default constructor
        /// <returns>The <see cref="ConstructorInfo"></see> for the default constructor or null if no default constructor
        /// exists</returns>
        public static ConstructorInfo GetDefaultConstructor(this Type type)
        {
#if NETFX_CORE
            return WinRTLegacy.TypeExtensions.GetConstructor(type, new Type[] { });
    #else
            return type.GetConstructor(new Type[] { });
#endif
        }

        /// <summary>
        /// Checks if the type has a constructor with no parameters
        /// </summary>
        /// <param name="type">The type for which to check if there's a default constructor
        /// <returns>Whether or not a default constructor exists for the given type</returns>
        public static bool HasDefaultConstructor(this Type type)
        {
            return type.GetDefaultConstructor() != null;
        }

        /// <summary>
        /// Gets all public static methods for a type
        /// </summary>
        /// <param name="typeInfo">The typeinfo for which to get the static methods
        /// <returns></returns>
        public static MethodInfo[] GetStaticMethods(this TypeInfo typeInfo)
        {
#if NETFX_CORE
            IEnumerable<methodinfo> methods = typeInfo.DeclaredMethods;
            List<methodinfo> staticMethods = new List<methodinfo>();
            foreach (MethodInfo method in methods)
                if (method.IsStatic && method.IsPublic)
                    staticMethods.Add(method);
            return staticMethods.ToArray();
#else
            return typeInfo.GetMethods(BindingFlags.Public | BindingFlags.Static);
#endif
        }

        /// <summary>
        /// Gets the assembly of the <see cref="TypeUtil"></see> class. In the .NET for UWP api you can only get the assembly
        /// of the running app.
        /// </summary>
        /// <returns></returns>
        public static List<assembly> GetAssemblies()
        {
            List<assembly> assemblies = new List<assembly>();
            assemblies.Add(typeof(TypeUtil).GetTypeInfoSafe().Assembly);
            return assemblies;
        }

        #endregion
    }

#if !NETFX_CORE
    /// <summary>
    /// Dummy class for TypeInfo of .NET for UWP
    /// </summary>
    public class TypeInfo
    {
        #region Fields

        private Type _type;

        #endregion

        #region Properties

        public string Name
        {
            get { return _type.Name; }
        }

        public Guid GUID
        {
            get { return _type.GUID; }
        }

        public Module Module
        {
            get { return _type.Module; }
        }

        public Assembly Assembly
        {
            get { return _type.Assembly; }
        }

        public string FullName
        {
            get { return _type.FullName; }
        }

        public string Namespace
        {
            get { return _type.Namespace; }
        }

        public string AssemblyQualifiedName
        {
            get { return _type.AssemblyQualifiedName; }
        }

        public bool IsAbstract
        {
            get { return _type.IsAbstract; }
        }

        public bool IsArray
        {
            get { return _type.IsArray; }
        }

        public bool IsEnum
        {
            get { return _type.IsEnum; }
        }

        public bool IsValueType
        {
            get { return _type.IsValueType; }
        }

        public bool IsClass
        {
            get { return _type.IsClass; }
        }

        public bool IsGenericType
        {
            get { return _type.IsGenericType; }
        }

        public bool IsInterface
        {
            get { return _type.IsInterface; }
        }

        public Type BaseType
        {
            get { return _type.BaseType; }
        }

        public Type UnderlyingSystemType
        {
            get { return _type.UnderlyingSystemType; }
        }

        public Type[] ImplementedInterfaces
        {
            get { return _type.GetInterfaces(); }
        }

        public Type[] GenericTypeArguments
        {
            get { return _type.GetGenericArguments(); }
        }

        public PropertyInfo[] DeclaredProperties
        {
            get { return _type.GetProperties(BindingFlags.Public); }
        }

        public Type[] DeclaredInterfaces
        {
            get { return _type.GetInterfaces(); }
        }

        public MethodInfo[] DeclaredMethods
        {
            get { return _type.GetMethods(); }
        }

        #endregion

        #region Setup

        public TypeInfo(Type t)
        {
            _type = t;
        }

        #endregion

        #region Public

        public Type AsType()
        {
            return _type;
        }

        public bool IsSubclassOf(Type type)
        {
            return _type.IsSubclassOf(type);
        }

        public object[] GetCustomAttributes(bool inherit)
        {
            return _type.GetCustomAttributes(inherit);
        }

        public bool IsDefined(Type attributeType, bool inherit)
        {
            return _type.IsDefined(attributeType, inherit);
        }

        public ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
        {
            return _type.GetConstructors(bindingAttr);
        }

        public Type GetInterface(string name, bool ignoreCase)
        {
            return _type.GetInterface(name, ignoreCase);
        }

        public Type[] GetInterfaces()
        {
            throw new NotImplementedException(
                "GetInterfaces method does not work in .NET core, please use the ImplementedInterfaces property");
        }

        public EventInfo GetEvent(string name, BindingFlags bindingAttr)
        {
            return _type.GetEvent(name, bindingAttr);
        }

        public EventInfo[] GetEvents(BindingFlags bindingAttr)
        {
            return _type.GetEvents(bindingAttr);
        }

        public Type[] GetNestedTypes(BindingFlags bindingAttr)
        {
            return _type.GetNestedTypes(bindingAttr);
        }

        public Type GetNestedType(string name, BindingFlags bindingAttr)
        {
            return _type.GetNestedType(name, bindingAttr);
        }

        public Type GetElementType()
        {
            return _type.GetElementType();
        }

        public PropertyInfo[] GetProperties(BindingFlags bindingAttr)
        {
            return _type.GetProperties(bindingAttr);
        }

        public MethodInfo[] GetMethods(BindingFlags bindingAttr)
        {
            return _type.GetMethods(bindingAttr);
        }

        public FieldInfo GetField(string name, BindingFlags bindingAttr)
        {
            return _type.GetField(name, bindingAttr);
        }

        public FieldInfo[] GetFields(BindingFlags bindingAttr)
        {
            return _type.GetFields(bindingAttr);
        }

        public MemberInfo[] GetMembers(BindingFlags bindingAttr)
        {
            return _type.GetMembers(bindingAttr);
        }

        public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
            object[] args,
            ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
        {
            return _type.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
        }

        public object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            return _type.GetCustomAttributes(attributeType, inherit);
        }

        public bool IsAssignableFrom(TypeInfo type)
        {
            return _type.IsAssignableFrom(type.AsType());
        }

        public PropertyInfo GetDeclaredProperty(string name)
        {
            return _type.GetProperty(name);
        }

        public Type GetGenericTypeDefinition()
        {
            return _type.GetGenericTypeDefinition();
        }

        #endregion

        #region Protected

        protected ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder,
            CallingConventions callConvention,
            Type[] types, ParameterModifier[] modifiers)
        {
            return _type.GetConstructor(bindingAttr, binder, callConvention, types, modifiers);
        }

        protected TypeAttributes GetAttributeFlagsImpl()
        {
            return _type.Attributes;
        }

        protected bool IsArrayImpl()
        {
            return _type.IsArray;
        }

        protected bool IsByRefImpl()
        {
            return _type.IsByRef;
        }

        protected bool IsPointerImpl()
        {
            return _type.IsPointer;
        }

        protected bool IsPrimitiveImpl()
        {
            return _type.IsPrimitive;
        }

        protected bool IsCOMObjectImpl()
        {
            return _type.IsCOMObject;
        }

        protected MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder,
            CallingConventions callConvention,
            Type[] types, ParameterModifier[] modifiers)
        {
            return _type.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers);
        }

        protected bool HasElementTypeImpl()
        {
            return _type.HasElementType;
        }

        protected PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder,
            Type returnType, Type[] types,
            ParameterModifier[] modifiers)
        {
            return _type.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
        }

        #endregion
    }
#endif
}

Een aantal plugins (zoals YamlDotNet) hadden al een oplossing gemaakt voor dit probleem. Echter doet elke plugin dit anders. De 'TypeUtil' klasse moet als universele oplossing gebruikt worden om code efficient te houden en om te zorgen dat alle problemen overal hetzelfde worden opgelost. Daarom is ervoor gekozen de metode 'GetTypeInfoSafe' te noemen in plaats van 'GetTypeInfo'. 'GetTypeInfo' wordt al door andere plugins gebruikt, daardoor kunnen referenties elkaar in de weg zitten. Op deze manier wordt altijd de goede methode gebruikt.


Naast de verplaatste code van Type reflection, is het ook niet meer mogelijk om op een delegate de 'Method.Invoke' methode aan te roepen. In plaats daarvan moet de callback direct aangeroepen worden. Bijvoorbeeld:

public void CallCallback(Action<bool> callback) 
{
    // Op deze manier kan een callback niet gecalled worden
    if (callback != null)
        callback.Method.Invoke(callback.Sender, new object[] { false });

    // Callbacks moeten op deze manier aangeroepen worden
    if (callback != null)
        callback(true);
}

Nu de buildingblocks zijn aangepast om deze veranderingen te ondersteunen kunnen ze voor beide scriptingbackends gebruikt worden.


Comments