Closed No3371 closed 5 years ago
Removing all the .Where() will results in insane amount of GC. To address this, I've come up with 2 ideas:
Ya, makes sense. Will see what you come up with, thanks! It could also work by adding some text file to read the custom assemblies from. Or, in hopes that it does not change too often, simply actively exclude assemblies we know are NOT user made.
Any news? Otherwise, at it is quite an edge-case, I would say you would indeed need to edit the code at that point...
I think this issue should remain open so someone can take a look at it in the future. I don't think this is an edge-case.
Custom assemblies are very much in use with Unity. Newer Unity components all use custom assemblies and in time asset store packages will also become custom assemblies.
A good start here is to ignore all assemblies with names starting with Unity. (Llot's of new packages use names like Unity.someComponent), UnityEngine. and UnityEditor. (all including the dot).
For newer .net, we also could ignore assemblies that are dynamic (check against Assembly.IsDynamic).
This will probably reduce the pressure by a lot.
As a last measure, if this is called more than once per type (I didn't study the code yet), it can be cached to a Dictionary<Type, Type[]> to improve query performance even further, at the cost of some memory.
Yeah, I have working code, which is basically a whitelist string array and some
List
Also I don't consider this a edge-class either due to Assembly Definition Files is a major feature after 2017 version (in my opinion).
Alright, alright:) No promises as to when I can get to it, currently in final essay phase. No problem though since you have code, thanks!
This suddenly comes to my mind. Here's the RefelctionUtility I modified last year, ugly but works, have to edit the assemblyFilters array to the assembly names desired.
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace NodeEditorFramework.Utilities
{
public static class ReflectionUtility
{
public static string[] assemblyFilters = new string[3] { "Story", "NodeEditor" };
static readonly Assembly[] currentDomainAssembliesCache;
public static Assembly[] CurrentDomainAssembliesCache
{
get { return currentDomainAssembliesCache; }
}
static readonly List<Type> nodeTypes;
public static List<Type> NodeTypes { get { return nodeTypes; }}
static readonly List<Type> nodeCanvasTypes;
public static List<Type> NodeCanvasTypes { get { return nodeCanvasTypes; }}
static readonly List<Type> connectionPortStyleTypes;
public static List<Type> ConnectionPortStyleTypes { get { return connectionPortStyleTypes; }}
static readonly List<Type> importExportType;
public static List<Type> ImportExportType { get { return importExportType; }}
static readonly List<MethodInfo> usedByInputSystem;
public static List<MethodInfo> UsedByInputSystem { get { return usedByInputSystem; }}
static ReflectionUtility ()
{
DateTime time = DateTime.Now;
currentDomainAssembliesCache = AppDomain.CurrentDomain.GetAssemblies().Where(a => assemblyFilters.Any(f => a.FullName.Contains(f))).ToArray();
// Debug.Log("GetAssemblies:" + (DateTime.Now - time).TotalMilliseconds + "ms");
nodeTypes = new List<Type>(50);
nodeCanvasTypes = new List<Type>(10);
connectionPortStyleTypes = new List<Type>(10);
importExportType = new List<Type>(3);
usedByInputSystem = new List<MethodInfo>(50);
foreach (Assembly assembly in currentDomainAssembliesCache)
{
time = DateTime.Now;
foreach (Type type in assembly.GetTypes())
{
if (IsValidType<Node>(type)) nodeTypes.Add(type);
if (IsValidType<NodeCanvas>(type, typeof(NodeCanvasTypeAttribute))) nodeCanvasTypes.Add(type);
if (IsValidType<ConnectionPortStyle>(type)) connectionPortStyleTypes.Add(type);
if (IsValidType<NodeEditorFramework.IO.ImportExportFormat>(type)) importExportType.Add(type);
foreach (MethodInfo method in type.GetMethods (BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
{
if (method.GetCustomAttributes(true).Any(t => t.GetType() == typeof(EventHandlerAttribute) || t.GetType() == typeof(HotkeyAttribute) || t.GetType() == typeof(ContextEntryAttribute) || t.GetType() == typeof(ContextFillerAttribute))) usedByInputSystem.Add(method);
}
}
// Debug.Log("Types in assembly " + assembly.FullName + ":" + (DateTime.Now - time).TotalMilliseconds + "ms");
}
}
public class ReflectionSearchIgnoreAttribute : Attribute
{
public ReflectionSearchIgnoreAttribute () { }
}
static bool IsValidType<T> (Type type, Type hasAttr = null)
{
if (type.IsClass
&& !type.IsAbstract
&& type.IsSubclassOf(typeof(T))
&& ((hasAttr == null)? true : (type.GetCustomAttributes (hasAttr, false).Length > 0))
&& !(type.GetCustomAttributes (typeof(ReflectionSearchIgnoreAttribute), false).Length > 0))
return true;
else return false;
}
/// <summary>
/// Gets all non-abstract types extending the given base type
/// </summary>
public static Type[] getSubTypes (Type baseType)
{
return CurrentDomainAssembliesCache
.SelectMany ((Assembly assembly) => assembly.GetTypes ()
.Where ((Type T) =>
(T.IsClass && !T.IsAbstract)
&& T.IsSubclassOf (baseType)
&& !T.GetCustomAttributes (typeof(ReflectionSearchIgnoreAttribute), false).Any ())
).ToArray ();
}
/// <summary>
/// Gets all non-abstract types extending the given base type and with the given attribute
/// </summary>
public static Type[] getSubTypes (Type baseType, Type hasAttribute)
{
return CurrentDomainAssembliesCache
.SelectMany ((Assembly assembly) => assembly.GetTypes ()
.Where ((Type T) =>
(T.IsClass && !T.IsAbstract)
&& T.IsSubclassOf (baseType)
&& T.GetCustomAttributes (hasAttribute, false).Any ()
&& !T.GetCustomAttributes (typeof(ReflectionSearchIgnoreAttribute), false).Any ())
).ToArray ();
}
/// <summary>
/// Returns all fields that should be serialized in the given type
/// </summary>
public static FieldInfo[] getSerializedFields (Type type)
{
return type.GetFields (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where ((FieldInfo field) =>
(field.IsPublic && !field.GetCustomAttributes (typeof(NonSerializedAttribute), true).Any ())
|| field.GetCustomAttributes (typeof(SerializeField), true).Any ()
&& !field.GetCustomAttributes (typeof(ReflectionSearchIgnoreAttribute), false).Any ())
.ToArray ();
}
/// <summary>
/// Returns all fields that should be serialized in the given type, minus the fields declared in or above the given base type
/// </summary>
public static FieldInfo[] getSerializedFields (Type type, Type hiddenBaseType)
{
return type.GetFields (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where ((FieldInfo field) =>
(hiddenBaseType == null || !field.DeclaringType.IsAssignableFrom (hiddenBaseType))
&& ((field.IsPublic && !field.GetCustomAttributes (typeof(NonSerializedAttribute), true).Any ())
|| field.GetCustomAttributes (typeof(SerializeField), true).Any ()
&& !field.GetCustomAttributes (typeof(ReflectionSearchIgnoreAttribute), false).Any ()))
.ToArray ();
}
/// <summary>
/// Gets all fields in the classType of the specified fieldType
/// </summary>
public static FieldInfo[] getFieldsOfType (Type classType, Type fieldType)
{
return classType.GetFields (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where ((FieldInfo field) =>
field.FieldType == fieldType || field.FieldType.IsSubclassOf (fieldType)
&& !field.GetCustomAttributes (typeof(ReflectionSearchIgnoreAttribute), false).Any ())
.ToArray ();
}
}
}
To adapt to this change to reflection utility and support adf, you have to modify places also using refelctions.
Take NodeEditorInputSystem for example:
All the others that have to be modified like this are located inside CoreExtension folder.
Assembly Definition File create assembly dll file with custom name.