ฉันจะโหลดประเภททั่วไปที่มีประเภทที่ซ้อนกันจากแอสเซมบลีที่โหลดแบบไดนามิกได้อย่างไร

ในแอสเซมบลีที่โหลดลงใน AppDomain ปัจจุบันโดยใช้ Assembly.LoadFrom ฉันมีรหัสต่อไปนี้:

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

EnumConverter‹T› ทั่วไปถูกกำหนดไว้ในแอสเซมบลีที่ส่งคืนโดยการเรียก Assembly.GetEntryAssembly() เมื่อฉันอ่านแอตทริบิวต์ TypeConverter ณ รันไทม์ ฉันได้รับชื่อเต็มของประเภทที่มีลักษณะดังนี้:

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

อย่างไรก็ตาม เมื่อฉันเรียก Type.GetType() บนสตริง จะส่งกลับค่าว่าง ฉันคาดว่าจะสามารถรับอินสแตนซ์ของ System.Type ได้ โปรดทราบว่า MyDynamicAssembly ไม่ได้ถูกสร้างขึ้นโดย Reflection.Emit (ชี้ให้เห็นในบทความ MSDN ที่นี่)

รหัสนี้สร้างขึ้นบน Visual Studio 2005 โดยใช้ .NET Framework 2.0

มีใครพบวิธีแก้ปัญหาสำหรับข้อบกพร่อง/ข้อจำกัดนี้หรือไม่ มันได้รับการแก้ไขใน 3.5 หรือไม่?


person Dave    schedule 19.03.2009    source แหล่งที่มา


คำตอบ (1)


หลังจากติดตามลูกค้าเป้าหมายจาก การโพสต์ฟอรัม MSDN ฉันสามารถรวบรวมโซลูชันทั่วไปสำหรับการโหลดประเภททั่วไปที่กำหนดเองจากแอสเซมบลีที่โหลดขณะรันไทม์ ฉันหวังว่านี่จะช่วยบางคนได้ (และบางที Microsoft อาจจะใส่สิ่งที่เทียบเท่ากับ. 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 ต่อไปนี้:

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

หมายเหตุ: คุณควรใส่เครื่องหมายตัวจัดการเล็กน้อยเมื่อพยายามใช้การทดสอบนี้ ไม่เช่นนั้น Type.GetType() จะถูกเรียกแทนโค้ดการแยกวิเคราะห์จริง

person Dave    schedule 19.03.2009