2009-03-19 3 views
3

Assembly.LoadFrom를 사용하여 현재 AppDomain으로로드 된 어셈블리에 다음 코드가 있습니다.동적으로로드 된 어셈블리에서 중첩 형식을 포함하는 제네릭 형식을로드하려면 어떻게합니까?

[TypeConverter(typeof(EnumConverter<Shapes>))] 
public enum Shapes 
{ 
    Triangle, 
    Square, 
    Circle 
} 

Generate EnumConverter <T>은 Assembly.GetEntryAssembly()를 호출하여 반환 된 어셈블리에서 정의됩니다. 런타임에 TypeConverter 특성을 읽을 때 뭔가 모양의 형식의 전체 이름을 얻을 :

MyAssembly.EnumConverter`1[[MyDynamicAssembly.Shapes, MyDynamicAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] 

그러나 문자열에 Type.GetType() 호출 할 때 null을 반환합니다. 나는 System.Type의 인스턴스를 얻을 수있을 것으로 예상했다. 또한 MyDynamicAssembly는 Reflection.Emit (MSDN 기사 here에서 지적한)에 의해 생성되지 않았습니다.

이 코드는 .NET Framework 2.0을 사용하여 Visual Studio 2005에서 생성되었습니다.

누구나이 버그/제한에 대한 해결책을 찾았습니까? 3.5로 수정 되었습니까?

답변

5

MSDN forum posting의 리드를 따라 가면서 런타임시로드 된 어셈블리에서 임의의 제네릭 유형을로드하기위한 일반적인 솔루션을 조합 할 수있었습니다.

[Test] 
public void GetType_DictionaryOfStringAndDictionaryOfInt32AndKeyValuePairOfStringAndListOfInt32() 
{ 
    Dictionary<string, Dictionary<int, KeyValuePair<string, List<int>>>> obj = 
    new Dictionary<string, Dictionary<int, KeyValuePair<string, List<int>>>>(); 

    string typeName = obj.GetType().FullName; 
    Type type = TypeHelpers.GetType(typeName, true, false); 

    Assert.IsTrue(type.Equals(obj.GetType())); 
} 
: (? 어쩌면 마이크로 소프트 .NET 4.0에 상응하는 뭔가를 넣어 것입니다) 나는

public static class TypeHelpers 
{ 
    /// <summary> 
    /// Gets the System.Type with the specified name, performing a case-sensitive search. 
    /// </summary> 
    /// <param name="typeName">The assembly-qualified name of the type to get. See System.Type.AssemblyQualifiedName.</param> 
    /// <param name="throwOnError">Whether or not to throw an exception or return null if the type was not found.</param> 
    /// <param name="ignoreCase">Whether or not to perform a case-insensitive search.</param> 
    /// <returns>The System.Type with the specified name.</returns> 
    /// <remarks> 
    /// This method can load types from dynamically loaded assemblies as long as the referenced assembly 
    /// has already been loaded into the current AppDomain. 
    /// </remarks> 
    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) 
      List<Type> list = new List<Type>(); 
      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<string> 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에 테스트로 된이 사람들을 도움이되기를 바랍니다

참고 :이 테스트를 사용하려고 할 때 사소한 처리기를 주석 처리해야합니다. 그렇지 않으면 실제 파싱 코드 대신 Type.GetType()이 호출됩니다.

관련 문제