|  |  |  | @ -45,11 +45,23 @@ namespace TNodeCore.Runtime.RuntimeCache{ | 
			
		
	
		
			
				
					|  |  |  |  |         public object Convert(object value){ | 
			
		
	
		
			
				
					|  |  |  |  |             return _converter.Convert((T1)value); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     //Store a t1 to t2 conversion but use two way converter's convert back method | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     internal class PortConverterHelperReverse<T1, T2> : IPortConverterHelper{ | 
			
		
	
		
			
				
					|  |  |  |  |         private readonly TwoWayPortTypeConversion<T2, T1> _converter; | 
			
		
	
		
			
				
					|  |  |  |  |         public object Convert(object value){ | 
			
		
	
		
			
				
					|  |  |  |  |             return _converter.ConvertBack((T1)value); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         public PortConverterHelperReverse(Type type){ | 
			
		
	
		
			
				
					|  |  |  |  |             _converter = Activator.CreateInstance(type) as TwoWayPortTypeConversion<T2, T1>; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     internal interface IPortConverterHelper{ | 
			
		
	
		
			
				
					|  |  |  |  |         public object Convert(object value); | 
			
		
	
		
			
				
					|  |  |  |  |     | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     public class PropertyNotFoundException : Exception{ | 
			
		
	
	
		
			
				
					|  |  |  | @ -126,18 +138,17 @@ namespace TNodeCore.Runtime.RuntimeCache{ | 
			
		
	
		
			
				
					|  |  |  |  |             else{ | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |  |         private void CacheRuntimePortTypeConversion(Type type){ | 
			
		
	
		
			
				
					|  |  |  |  |             if (type.BaseType == null) return; | 
			
		
	
		
			
				
					|  |  |  |  |             if (type.BaseType != null){ | 
			
		
	
		
			
				
					|  |  |  |  |                 var genericType = type.BaseType.GetGenericTypeDefinition(); | 
			
		
	
		
			
				
					|  |  |  |  |                 if (genericType != typeof(PortTypeConversion<,>)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 if (genericType != typeof(PortTypeConversion<,>)|| genericType != typeof(TwoWayPortTypeConversion<,>)){ | 
			
		
	
		
			
				
					|  |  |  |  |                     return; | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             else{ | 
			
		
	
		
			
				
					|  |  |  |  |                 return; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |  |             //Forward direction | 
			
		
	
		
			
				
					|  |  |  |  |             var type1 = type.BaseType.GetGenericArguments()[0]; | 
			
		
	
		
			
				
					|  |  |  |  |             var type2 = type.BaseType.GetGenericArguments()[1]; | 
			
		
	
		
			
				
					|  |  |  |  |             var specificType = typeof(PortConverterHelper<,>).MakeGenericType(type1, type2); | 
			
		
	
	
		
			
				
					|  |  |  | @ -149,17 +160,70 @@ namespace TNodeCore.Runtime.RuntimeCache{ | 
			
		
	
		
			
				
					|  |  |  |  |                 CachedPortConverters.Add(type1,new Dictionary<Type,IPortConverterHelper>()); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             CachedPortConverters[type1].Add(type2,instance); | 
			
		
	
		
			
				
					|  |  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |  |             //Reverse direction | 
			
		
	
		
			
				
					|  |  |  |  |             if(type.BaseType.GetGenericTypeDefinition()==typeof(TwoWayPortTypeConversion<,>)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 var specificTypeReverse = typeof(PortConverterHelperReverse<,>).MakeGenericType(type2, type1); | 
			
		
	
		
			
				
					|  |  |  |  |                 var instanceReverse = Activator.CreateInstance(specificTypeReverse, type) as IPortConverterHelper; | 
			
		
	
		
			
				
					|  |  |  |  |                 if (instanceReverse == null){ | 
			
		
	
		
			
				
					|  |  |  |  |                     return; | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |                 if (!CachedPortConverters.ContainsKey(type2)){ | 
			
		
	
		
			
				
					|  |  |  |  |                     CachedPortConverters.Add(type2,new Dictionary<Type,IPortConverterHelper>()); | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |                 CachedPortConverters[type2].Add(type1,instanceReverse); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |         private readonly Dictionary<Tuple<Type,Type>,bool> _possibleImplicitConversions = new (); | 
			
		
	
		
			
				
					|  |  |  |  |         private bool HasImplicitConversion(Type baseType, Type targetType){ | 
			
		
	
		
			
				
					|  |  |  |  |             var tuple = new Tuple<Type, Type>(baseType, targetType); | 
			
		
	
		
			
				
					|  |  |  |  |             if (_possibleImplicitConversions.ContainsKey(tuple)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 return _possibleImplicitConversions[tuple]; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             var res =baseType.GetMethods(BindingFlags.Public | BindingFlags.Static) | 
			
		
	
		
			
				
					|  |  |  |  |                 .Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == targetType) | 
			
		
	
		
			
				
					|  |  |  |  |                 .Any(mi => { | 
			
		
	
		
			
				
					|  |  |  |  |                     ParameterInfo pi = mi.GetParameters().FirstOrDefault(); | 
			
		
	
		
			
				
					|  |  |  |  |                     return pi != null && pi.ParameterType == baseType; | 
			
		
	
		
			
				
					|  |  |  |  |                 }); | 
			
		
	
		
			
				
					|  |  |  |  |             return _possibleImplicitConversions[tuple] = res; | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         private void CachingImplicitConversion(Type baseType, Type targetType){ | 
			
		
	
		
			
				
					|  |  |  |  |             if (HasImplicitConversion(baseType, targetType)) return; | 
			
		
	
		
			
				
					|  |  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |  |             //Create Implicit Conversion Helper that caches the implicit cast function | 
			
		
	
		
			
				
					|  |  |  |  |             var typeConverter = Activator.CreateInstance(typeof(ImplicitConversionHelper<,>).MakeGenericType(baseType, targetType)) as IPortConverterHelper; | 
			
		
	
		
			
				
					|  |  |  |  |              | 
			
		
	
		
			
				
					|  |  |  |  |             if (!CachedPortConverters.ContainsKey(baseType)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 CachedPortConverters.Add(baseType,new Dictionary<Type,IPortConverterHelper>()); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             CachedPortConverters[baseType].Add(targetType,typeConverter); | 
			
		
	
		
			
				
					|  |  |  |  |            | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |          | 
			
		
	
		
			
				
					|  |  |  |  |         public object GetConvertedValue(Type from,Type to,object value){ | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |             if(!CachedPortConverters.ContainsKey(from)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 throw new ConversionFailedException("No converter found for type "+from); | 
			
		
	
		
			
				
					|  |  |  |  |                 //Find the cached port failed ,check if there is an implicit conversion | 
			
		
	
		
			
				
					|  |  |  |  |                 //This inner cache method would only run once,so add a guard to prevent it run again,even though the function itself has a guard statement. | 
			
		
	
		
			
				
					|  |  |  |  |                 if(HasImplicitConversion(from,to)){ | 
			
		
	
		
			
				
					|  |  |  |  |                     CachingImplicitConversion(from,to); | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             if(!CachedPortConverters[from].ContainsKey(to)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 //Just like above, this function should be checked in here too | 
			
		
	
		
			
				
					|  |  |  |  |                 if(HasImplicitConversion(from,to)){ | 
			
		
	
		
			
				
					|  |  |  |  |                     CachingImplicitConversion(from,to); | 
			
		
	
		
			
				
					|  |  |  |  |                 } | 
			
		
	
		
			
				
					|  |  |  |  |                 return value; | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             return CachedPortConverters[from][to].Convert(value); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         private bool GetImplcitConvertedValue(Type from, Type to){ | 
			
		
	
		
			
				
					|  |  |  |  |             throw new NotImplementedException(); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         public List<Type> GetSupportedTypes(Type type){ | 
			
		
	
		
			
				
					|  |  |  |  |             if(!CachedPortConverters.ContainsKey(type)){ | 
			
		
	
		
			
				
					|  |  |  |  |                 return null; | 
			
		
	
	
		
			
				
					|  |  |  | @ -248,6 +312,25 @@ namespace TNodeCore.Runtime.RuntimeCache{ | 
			
		
	
		
			
				
					|  |  |  |  |    | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     public class ImplicitConversionHelper<T1,T2> : IPortConverterHelper{ | 
			
		
	
		
			
				
					|  |  |  |  |         public Func<T1, T2> ConvertFunc; | 
			
		
	
		
			
				
					|  |  |  |  |         public ImplicitConversionHelper(){ | 
			
		
	
		
			
				
					|  |  |  |  |             //Caching the implicit method that converts t1 to t2 | 
			
		
	
		
			
				
					|  |  |  |  |             var method = typeof(T2).GetMethod("op_Implicit", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(T1) }, null); | 
			
		
	
		
			
				
					|  |  |  |  |             if (method == null){ | 
			
		
	
		
			
				
					|  |  |  |  |                 //Search it in T1 | 
			
		
	
		
			
				
					|  |  |  |  |                 method = typeof(T1).GetMethod("op_Implicit", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(T2) }, null); | 
			
		
	
		
			
				
					|  |  |  |  |             } | 
			
		
	
		
			
				
					|  |  |  |  |             //Create the delegate | 
			
		
	
		
			
				
					|  |  |  |  |             if (method != null)  | 
			
		
	
		
			
				
					|  |  |  |  |                 ConvertFunc = (Func<T1, T2>) Delegate.CreateDelegate(typeof(Func<T1, T2>), method); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         public object Convert(object value){ | 
			
		
	
		
			
				
					|  |  |  |  |             return ConvertFunc((T1) value); | 
			
		
	
		
			
				
					|  |  |  |  |         } | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     public class ConversionFailedException : Exception{ | 
			
		
	
		
			
				
					|  |  |  |  |         public ConversionFailedException(string noConverterFoundForType):base(noConverterFoundForType){ | 
			
		
	
		
			
				
					|  |  |  |  |              | 
			
		
	
	
		
			
				
					|  |  |  | 
 |