在加载到使用当前的AppDomain的组件Assembly.LoadFrom,我有以下代码:
[TypeConverter(typeof(EnumConverter))] public enum Shapes { Triangle, Square, Circle }
通用EnumConverter
MyAssembly.EnumConverter`1[[MyDynamicAssembly.Shapes, MyDynamicAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
但是,当我在字符串上调用Type.GetType()时,它返回null.我希望能够获得System.Type的实例.还要注意的是MyDynamicAssembly不是由Reflection.Emit的产生(在MSDN文章中指出在这里).
此代码是使用.NET Framework 2.0在Visual Studio 2005上生成的.
有没有人找到这个bug /限制的解决方法?它是否已在3.5中修复?
在跟踪MSDN论坛帖子的主角后,我能够整合一个通用解决方案,用于从运行时加载的程序集中加载任意泛型类型.我希望这可以帮助一些人(也许微软会把相同的东西放到.NET 4.0中?)
public static class TypeHelpers { ////// Gets the System.Type with the specified name, performing a case-sensitive search. /// /// The assembly-qualified name of the type to get. See System.Type.AssemblyQualifiedName. /// Whether or not to throw an exception or return null if the type was not found. /// Whether or not to perform a case-insensitive search. ///The System.Type with the specified name. ////// This method can load types from dynamically loaded assemblies as long as the referenced assembly /// has already been loaded into the current AppDomain. /// public static Type GetType(string typeName, bool throwOnError, bool ignoreCase) { if(string.IsNullOrEmpty(typeName)) throw new ArgumentNullException("typeName"); // handle the trivial case Type type; if((type = Type.GetType(typeName, false, ignoreCase)) != null) return type; // otherwise, perform the recursive search try { return GetTypeFromRecursive(typeName, ignoreCase); } catch(Exception e) { if(throwOnError) throw; } return null; } #region Private Static Helper Methods private static Type GetTypeFromRecursive(string typeName, bool ignoreCase) { int startIndex = typeName.IndexOf('['); int endIndex = typeName.LastIndexOf(']'); if(startIndex == -1) { // try to load the non-generic type (e.g. System.Int32) return TypeHelpers.GetNonGenericType(typeName, ignoreCase); } else { // determine the cardinality of the generic type int cardinalityIndex = typeName.IndexOf('`', 0, startIndex); string cardinalityString = typeName.Substring(cardinalityIndex + 1, startIndex - cardinalityIndex - 1); int cardinality = int.Parse(cardinalityString); // get the FullName of the non-generic type (e.g. System.Collections.Generic.List`1) string fullName = typeName.Substring(0, startIndex); if(typeName.Length - endIndex - 1 > 0) fullName += typeName.Substring(endIndex + 1, typeName.Length - endIndex - 1); // parse the child type arguments for this generic type (recursive) Listlist = new List (); string typeArguments = typeName.Substring(startIndex + 1, endIndex - startIndex - 1); foreach(string item in EachAssemblyQualifiedName(typeArguments, cardinality)) { Type typeArgument = GetTypeFromRecursive(item, ignoreCase); list.Add(typeArgument); } // construct the generic type definition return TypeHelpers.GetNonGenericType(fullName, ignoreCase).MakeGenericType(list.ToArray()); } } private static IEnumerable EachAssemblyQualifiedName(string s, int count) { Debug.Assert(count != 0); Debug.Assert(string.IsNullOrEmpty(s) == false); Debug.Assert(s.Length > 2); // e.g. "[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]" // e.g. "[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]" // e.g. "[System.Collections.Generic.KeyValuePair`2[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]" int startIndex = 0; int bracketCount = 0; while(count > 0) { bracketCount = 0; for(int i = startIndex; i < s.Length; i++) { switch(s[i]) { case '[': bracketCount++; continue; case ']': if(--bracketCount == 0) { string item = s.Substring(startIndex + 1, i - startIndex - 1); yield return item; startIndex = i + 2; } break; default: continue; } } if(bracketCount != 0) { const string SR_Malformed = "The brackets are unbalanced in the string, '{0}'."; throw new FormatException(string.Format(SR_Malformed, s)); } count--; } } private static Type GetNonGenericType(string typeName, bool ignoreCase) { // assume the type information is not a dynamically loaded assembly Type type = Type.GetType(typeName, false, ignoreCase); if(type != null) return type; // otherwise, search the assemblies in the current AppDomain for the type int assemblyFullNameIndex = typeName.IndexOf(','); if(assemblyFullNameIndex != -1) { string assemblyFullName = typeName.Substring(assemblyFullNameIndex + 2, typeName.Length - assemblyFullNameIndex - 2); foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { if(assembly.GetName().FullName == assemblyFullName) { string fullName = typeName.Substring(0, assemblyFullNameIndex); type = assembly.GetType(fullName, false, ignoreCase); if(type != null) return type; } } } // no luck? blow up const string SR_TypeNotFound = "The type, '{0}', was not found."; throw new ArgumentException(string.Format(SR_TypeNotFound, typeName), "typeName"); } #endregion }
使用上面的场景和以下MbUnit测试测试了此代码:
[Test] public void GetType_DictionaryOfStringAndDictionaryOfInt32AndKeyValuePairOfStringAndListOfInt32() { Dictionary>>> obj = new Dictionary >>>(); string typeName = obj.GetType().FullName; Type type = TypeHelpers.GetType(typeName, true, false); Assert.IsTrue(type.Equals(obj.GetType())); }
注意:在尝试使用此测试时,您应该注释掉普通的处理程序,否则将调用Type.GetType()而不是实际的解析代码.