Skip to content
This repository has been archived by the owner on Oct 31, 2023. It is now read-only.

assembly.FullName.Contains ("Assembly") not working with Assembly Definition File #159

Closed
No3371 opened this issue Apr 8, 2018 · 9 comments

Comments

@No3371
Copy link

No3371 commented Apr 8, 2018

Assembly Definition File create assembly dll file with custom name.

@No3371
Copy link
Author

No3371 commented Apr 8, 2018

Removing all the .Where() will results in insane amount of GC.
To address this, I've come up with 2 ideas:

  1. Provide an interface for users to set a list of assembly name string filter to use by the .Where()
  2. Or do a lot of caching in ReflectionUtility, and don't re-setup if it's just a simple onFocus().
    Will provide working code later.

@Seneral
Copy link
Owner

Seneral commented Apr 8, 2018

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.

@Seneral
Copy link
Owner

Seneral commented Apr 20, 2018

Any news? Otherwise, at it is quite an edge-case, I would say you would indeed need to edit the code at that point...

@Seneral Seneral closed this as completed Apr 24, 2018
@SugoiDev
Copy link

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.

@No3371
Copy link
Author

No3371 commented Apr 25, 2018 via email

@No3371
Copy link
Author

No3371 commented Apr 25, 2018

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).

@Seneral Seneral reopened this Apr 25, 2018
@Seneral
Copy link
Owner

Seneral commented Apr 25, 2018

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!

@No3371
Copy link
Author

No3371 commented Mar 20, 2019

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 ();
		}
	}
}

@No3371 No3371 closed this as completed Mar 20, 2019
@No3371
Copy link
Author

No3371 commented Mar 20, 2019

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants