Добавлен , опубликован
В общем сегодня пришлось слишком много работать с рефлексией, доставая кучу интерналовских функций и полей из библиотеки. Работал себе, работал, а потом подумал - а чего бы не упростить?
Конечно, скорость страдает, но мне она была не принципиальна, ибо рефлексия так и так мне хороших результатов по скорости не даст.
В общем составил такой вот скриптик, хотите пользуйтесь, хотите нет:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;

public static class Reflection
{
    public static Assembly[] GetAllAssemblies()
    {
        return AppDomain.CurrentDomain.GetAssemblies();
    }

    public static Assembly GetUnityAssembly(UnityAssembly unityAssembly)
    {
        return Assembly.Load(unityAssemblies[(int) unityAssembly]);
    }

    private static string[] unityAssemblies = new[]
    {
        "Assembly-CSharp",
        "Assembly-CSharp-Editor",
        "UnityEngine",
        "UnityEditor"
    };

    public enum UnityAssembly
    {
        AssemblyCSharp, AssemblyCSharpEditor, UnityEngine, UnityEditor
    }

    public static Assembly[] GetReferencedAssemblies(UnityAssembly unityAssembly)
    {
        var assembly = GetUnityAssembly(unityAssembly);
        var list = new List<Assembly> {assembly};
        list.AddRange(assembly.GetReferencedAssemblies().Select(x => Assembly.Load(x)));
        return list.ToArray();
    }

    public static AdvancedClasses.MonadaReflectionInstance Go<TType>(TType instanceObject)
    {
        return Go(typeof(TType), instanceObject);
    }

    public static AdvancedClasses.MonadaReflectionInstance Go<TType>()
    {
        return new AdvancedClasses.MonadaReflectionInstance
        {
            instance = null,
            type = typeof (TType)
        };
    }

    public static AdvancedClasses.MonadaReflectionInstance Go(Type type, object instanceObject)
    {
        if (instanceObject != null)
            type = instanceObject.GetType();
        return new AdvancedClasses.MonadaReflectionInstance
        {
            instance = instanceObject,
            type = type
        };
    }

    public static AdvancedClasses.MonadaReflectionInstance Go(Type type)
    {
        return new AdvancedClasses.MonadaReflectionInstance
        {
            instance = null,
            type = type
        };
    }

    public static AdvancedClasses.MonadaReflectionInstance GoUnityEditor(string typename)
    {
        var editorAssembly = Assembly.GetAssembly(typeof (EditorWindow));
        var type = editorAssembly.GetType("UnityEditor." + typename, false, true);
        if (type == null)
            type = editorAssembly.GetType(typename);
        return Go(type);
    }

    public static class AdvancedClasses
    {
        public const BindingFlags ANY_MEMBER = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
        public abstract class MonadaReflectionBase
        {
            public Type type;
            public object instance;

            protected MonadaReflectionFieldInfo Return(FieldInfo field, Type type, object instance)
            {
                return new MonadaReflectionFieldInfo { member = field, type = type, instance = instance };
            }

            protected MonadaReflectionMethodInfo Return(MethodInfo method, Type type, object instance)
            {
                return new MonadaReflectionMethodInfo { member = method, type = type, instance = instance };
            }

            protected bool ArgNameEqual(MethodInfo method, string[] argNames)
            {
                var args = method.GetParameters();
                if (args.Length != argNames.Length)
                    return false;

                var result = true;
                for (int i = 0; i < args.Length; i++)
                    if (args[i].Name != argNames[i])
                        result = false;
                return result;
            }

            protected bool ArgTypeEqual(MethodInfo method, Type[] argTypes)
            {
                var args = method.GetParameters();
                if (args.Length != argTypes.Length)
                    return false;

                var result = true;
                for (int i = 0; i < args.Length; i++)
                    if (args[i].ParameterType != argTypes[i])
                        result = false;
                return result;
            }

            protected bool ArgObjEqual(MethodInfo method, object[] argsReal)
            {
                var args = method.GetParameters();
                if (args.Length != argsReal.Length)
                    return false;

                var result = true;
                for (int i = 0; i < args.Length; i++)
                    if (argsReal[i] != null && !argsReal[i].GetType().Is(args[i].ParameterType))
                        result = false;
                return result;
            }

            protected abstract Type _GetType();
            protected abstract object _GetInstance(params object[] args);

            public MonadaReflectionFieldInfo ToField(string fieldName)
            {
                var field = _GetType().GetField(fieldName, ANY_MEMBER);
                return Return(field, _GetType(), _GetInstance());
            }

            public MonadaReflectionMethodInfo ToMethod(string methodName)
            {
                var method = _GetType().GetMethod(methodName, ANY_MEMBER);
                return Return(method, _GetType(), _GetInstance());
            }

            public MonadaReflectionMethodInfo ToMethod(string methodName, params object[] args)
            {
                var method = _GetType().GetMethods(ANY_MEMBER).FirstOrDefault(x => ArgObjEqual(x, args));
                if (method == null)
                    throw new Exception("Method is null");
                var result = Return(method, _GetType(), _GetInstance());
                result.args = args;
                return result;
            }

            public MonadaReflectionMethodInfo ToMethod(string methodName, params string[] argNames)
            {
                var method = _GetType().GetMethods(ANY_MEMBER).FirstOrDefault(x => ArgNameEqual(x, argNames));
                if (method == null)
                    throw new Exception("Method is null");
                return Return(method, _GetType(), _GetInstance());
            }

            public MonadaReflectionMethodInfo ToMethod(string methodName, params Type[] argTypes)
            {
                var method = _GetType().GetMethods(ANY_MEMBER).FirstOrDefault(x => ArgTypeEqual(x, argTypes));
                if (method == null)
                    throw new Exception("Method is null");
                return Return(method, _GetType(), _GetInstance());
            }

            public MonadaReflectionMethodInfo ToMethod(string methodName, int argCount)
            {
                var method = _GetType().GetMethods(ANY_MEMBER).FirstOrDefault(x => x.GetParameters().Length == argCount);
                if (method == null)
                    throw new Exception("Method is null");
                return Return(method, _GetType(), _GetInstance());
            }
        }

        public class MonadaReflectionInstance : MonadaReflectionBase
        {
            protected override Type _GetType()
            {
                return type;
            }

            protected override object _GetInstance(params object[] args)
            {
                return instance;
            }

            public object ToEnumValue(int needValue)
            {
                return Enum.ToObject(type, needValue);
            }
        }

        public abstract class MonadaReflectionMember : MonadaReflectionBase
        {
            public MemberInfo member;
        }

        public class MonadaReflectionFieldInfo : MonadaReflectionMember
        {
            public new FieldInfo member { get { return (FieldInfo)base.member; } set { base.member = value; } }

            public object GetValue()
            {
                return member.GetValue(instance);
            }

            public T GetValue<T>()
            {
                var result = GetValue();
                if (result != null)
                    return (T)result;
                return default(T);
            }

            protected override Type _GetType()
            {
                return member.FieldType;
            }

            protected override object _GetInstance(params object[] args)
            {
                return GetValue();
            }
        }

        public class MonadaReflectionMethodInfo : MonadaReflectionMember
        {
            public new MethodInfo member { get { return (MethodInfo)base.member; } set { base.member = value; }} 
            
            public object[] args;
            public object Call(params object[] args)
            {
                if (!((args == null || args.Length == 0) && member.GetParameters().Length != 0))
                    this.args = args;
                return member.Invoke(instance, this.args);
            }

            protected override Type _GetType()
            {
                return member.ReturnType;
            }

            protected override object _GetInstance(params object[] args)
            {
                return Call(args);
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public static class ExtensionsReflection
{
    /// <summary> Возвращает типы которые наследовались от указанного типа (в пределах той же сборки)</summary>
    public static List<Type> GetDependents(this Type baseType)
    {
        return baseType.Assembly.GetTypes().Where(x => x.Is(baseType)).ToList();
    }

    /// <summary> Указывает, наследуется ли тип A от типа B. Аналог ключевого слова 'is' в рефлексии </summary>
    public static bool Is(this Type a, Type b)
    {
        return a == b || (a.BaseType != null && Is(a.BaseType, b));
    }

    public static bool Is<T>(this Type a)
    {
        return a.Is(typeof(T));
    }

    /// <summary> Возвращает экземпляр аттрибута AttrType, закрепленного за типом </summary>
    public static AttrType GetAttribute<AttrType>(this Type baseType) where AttrType : Attribute
    {
        var attrs = baseType.GetCustomAttributes(typeof(AttrType), false);
        if (attrs.Length > 0)
            return attrs[0] as AttrType;
        return null;
    }

    /// <summary> Возвращает экземпляр аттрибута AttrType, закрепленного за MemberInfo </summary>
    public static AttrType GetAttribute<AttrType>(this MemberInfo baseType) where AttrType : Attribute
    {
        var attrs = baseType.GetCustomAttributes(typeof(AttrType), false);
        if (attrs.Length > 0)
            return attrs[0] as AttrType;
        return null;
    }
}
В результате что вышло?
Покажу на примере
var editorAssembly = Assembly.GetAssembly(typeof (EditorWindow));
var typeShowMode = editorAssembly.GetType("UnityEditor.ShowMode");
var value = Enum.ToObject(typeShowMode, 3);
typeof (HintWindow)
    .GetMethod("ShowWithMode", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)
    .Invoke(hintWindow, new[] {value});
hintWindow.ShowTab();
var typeHostView = Assembly.GetAssembly(typeof(EditorWindow)).GetType("UnityEditor.HostView");
var mParent = hintWindow.GetType().GetField("m_Parent", internalInstance).GetValue(hintWindow);

typeHostView
    .GetMethod("AddToAuxWindowList", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance)
    .Invoke(mParent, new object[0]);
var enumValue = Reflection.GoUnityEditor("ShowMode").ToEnumValue(3);
Reflection.Go(hintWindow).ToMethod("ShowWithMode", enumValue).Call();
Reflection.Go(hintWindow).ToField("m_Parent").ToMethod("AddToAuxWindowList").Call();
Конечно, кода меньше. Но такие вещи как например сборки, типы и методы некоим способом не оптимизируется в вызовах и могут доставаться по несколько раз. Ну и плюс написал сугубо под свои нужды, над расширением не думал.

Вопросики

  • Работать - работает. А правильно ли сделал - хз. Может читатель чего посоветует, подскажет хорошие способы для формирования однострочников?
  • И еще - есть ли какие нибудь готовые решения? В том плане что разраб задумался над оптимизацией вызовов рефлексии.
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
29
9 лет назад
0
Так и не понял для чего это надо
0
27
9 лет назад
0
alexprey, ну смотри
Вот например я много знаю интерналовских и приватных функций которые в том же юнити нельзя вызвать напрямую. Но они очень нужны бывают, просто разрабы решили что это никому не пригодится
Вон пример кинул
чтобы обычным путем найти интерналовский метод в классе приватной переменной хранящейся в другом интерналовском классе приходится делать дико непростое выражение. То есть заместо простого
СтатическийКласс1.объект2.НужныйМетод()
Мы вынуждены писать дикие конструкции в рефлексии.
А эта фигня делает эти обращения проще, однострочником
Например
Go<СтатическийКласс1>().ToField("объект2").ToMethod("НужныйМетод").Call()
Все проще чем вручную вытаскивать классы, фильтровать списки членов и прочая хрень.
0
14
9 лет назад
0
Ох, стрёмно это. Cегодня они есть, а завтра нет, или зарефакторят им другую логику работы, интерфейс сменят. . К тому же медленней прямых вызовов на несколько порядков.
Что там в юнити такого полезного от любопытных спрятали?
0
27
9 лет назад
Отредактирован Devion
0
Kozinaka, очень многое, что вряд ли изменят.
Например теги GUILayout.Group, буфер обмена, ручной запуск ребилда, приоритеты и параметры окон, почти всю инфу касательно существующих окон - типа дебаг лога (нативный лог в юнити очень ограниченный, скажем нельзя по стеку вызовов обратиться к какой либо строке, зато самому такое написать можно). В целом часто чтобы сделать что то нестандартное или влияющее на сам юнити нужно обращаться к функциям, которые спрятаны инкапсуляцией.
Медленно, да, но во-первых - тут ничего не попишешь, во-вторых, это же для создания инструмента а не для конечного игрока делается, и то где это применяется часто иной способ реализовать такое либо отсутствует либо выйдет еще дороже.
Потому бегать от рефлексии глупо. Да и скажем если это какой нибудь алгоритм то там терпимо, так как он вызывается единожды, а не постоянно. Ну и на всякий пожарный всегда можно кешировать вычисления.
Ну и чаще все таки все остается как есть, хотя, конечно вероятность что завтра этих скрытых функций не станет всегда имеется. Но это на самом деле не шибко высокая вероятность за тот период времени когда в таком инструменте есть нужда. Ту же юньку 4 как я понимаю уже допиливать особо не будут, а будут уже делать пятую версию.
0
14
9 лет назад
0
Extravert, в качестве инструмента конечно! Даже если что-то и накроется, так всегда можно поправить.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.