Разрешить тип из имени класса в другой сборке - PullRequest
71 голосов
/ 18 августа 2010

У меня есть метод, в котором мне нужно разрешить тип класса. Этот класс существует в другой сборке с пространством имен, похожим на:

MyProject.Domain.Model

Я пытаюсь выполнить следующее:

Type.GetType("MyProject.Domain.Model." + myClassName);

Это прекрасно работает, если код, выполняющий это действие, находится в той же сборке, что и класс, тип которого я пытаюсь разрешить, однако, если мой класс находится в другой сборке, этот код завершается ошибкой.

Я уверен, что есть гораздо лучший способ выполнить эту задачу, но у меня не было большого опыта в разрешении сборок и обходе пространств имен внутри, чтобы определить тип класса, который я ищу. Любой совет или советы, чтобы выполнить эту задачу более изящно?

Ответы [ 6 ]

150 голосов
/ 18 августа 2010

Вам необходимо добавить имя сборки следующим образом:

Type.GetType("MyProject.Domain.Model." + myClassName + ", AssemblyName");

Чтобы избежать неоднозначности или если сборка находится в GAC, вы должны предоставить полное имя сборки, например:

Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
1 голос
/ 29 мая 2019

Подобно OP, мне нужно было загрузить ограниченное подмножество типов по имени (в моем случае все классы были в одной сборке и реализовали один и тот же интерфейс). У меня было много странных проблем при попытке использовать Type.GetType(string) для другой сборки (даже при добавлении AssemblyQualifiedName, как упоминалось в других статьях). Вот как я решил проблему:

Использование:

var mytype = TypeConverter<ICommand>.FromString("CreateCustomer");

Код:

    public class TypeConverter<BaseType>
        {
            private static Dictionary<string, Type> _types;
            private static object _lock = new object();

            public static Type FromString(string typeName)
            {
                if (_types == null) CacheTypes();

                if (_types.ContainsKey(typeName))
                {
                    return _types[typeName];
                }
                else
                {
                    return null;
                }
            }

            private static void CacheTypes()
            {
                lock (_lock)
                {
                    if (_types == null)
                    {
                        // Initialize the myTypes list.
                        var baseType = typeof(BaseType);
                        var typeAssembly = baseType.Assembly;
                        var types = typeAssembly.GetTypes().Where(t => 
                            t.IsClass && 
                            !t.IsAbstract && 
                            baseType.IsAssignableFrom(t));

                        _types = types.ToDictionary(t => t.Name);
                    }
                }
            }
        }

Очевидно, что вы можете настроить метод CacheTypes для проверки всех сборок в AppDomain или другой логики, которая лучше соответствует вашему сценарию использования. Если ваш вариант использования позволяет загружать типы из нескольких пространств имен, вы можете изменить ключ словаря, чтобы вместо него использовать тип FullName. Или, если ваши типы не наследуются от общего интерфейса или базового класса, вы можете удалить <BaseType> и изменить метод CacheTypes, чтобы использовать что-то вроде .GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")

1 голос
/ 26 ноября 2018

Это универсальное решение предназначено для людей, которым необходимо загрузить универсальные типы из динамических внешних ссылок на AssemblyQualifiedName, не зная, из какой сборки все части универсального типа поступают из:

    public static Type ReconstructType(string assemblyQualifiedName, bool throwOnError = true, params Assembly[] referencedAssemblies)
    {
        foreach (Assembly asm in referencedAssemblies)
        {
            var fullNameWithoutAssemblyName = assemblyQualifiedName.Replace($", {asm.FullName}", "");
            var type = asm.GetType(fullNameWithoutAssemblyName, throwOnError: false);
            if (type != null) return type;
        }

        if (assemblyQualifiedName.Contains("[["))
        {
            Type type = ConstructGenericType(assemblyQualifiedName, throwOnError);
            if (type != null)
                return type;
        }
        else
        {
            Type type = Type.GetType(assemblyQualifiedName, false);
            if (type != null)
                return type;
        }

        if (throwOnError)
            throw new Exception($"The type \"{assemblyQualifiedName}\" cannot be found in referenced assemblies.");
        else
            return null;
    }

    private static Type ConstructGenericType(string assemblyQualifiedName, bool throwOnError = true)
    {
        Regex regex = new Regex(@"^(?<name>\w+(\.\w+)*)`(?<count>\d)\[(?<subtypes>\[.*\])\](, (?<assembly>\w+(\.\w+)*)[\w\s,=\.]+)$?", RegexOptions.Singleline | RegexOptions.ExplicitCapture);
        Match match = regex.Match(assemblyQualifiedName);
        if (!match.Success)
            if (!throwOnError) return null;
            else throw new Exception($"Unable to parse the type's assembly qualified name: {assemblyQualifiedName}");

        string typeName = match.Groups["name"].Value;
        int n = int.Parse(match.Groups["count"].Value);
        string asmName = match.Groups["assembly"].Value;
        string subtypes = match.Groups["subtypes"].Value;

        typeName = typeName + $"`{n}";
        Type genericType = ReconstructType(typeName, throwOnError);
        if (genericType == null) return null;

        List<string> typeNames = new List<string>();
        int ofs = 0;
        while (ofs < subtypes.Length && subtypes[ofs] == '[')
        {
            int end = ofs, level = 0;
            do
            {
                switch (subtypes[end++])
                {
                    case '[': level++; break;
                    case ']': level--; break;
                }
            } while (level > 0 && end < subtypes.Length);

            if (level == 0)
            {
                typeNames.Add(subtypes.Substring(ofs + 1, end - ofs - 2));
                if (end < subtypes.Length && subtypes[end] == ',')
                    end++;
            }

            ofs = end;
            n--;  // just for checking the count
        }

        if (n != 0)
            // This shouldn't ever happen!
            throw new Exception("Generic type argument count mismatch! Type name: " + assemblyQualifiedName);  

        Type[] types = new Type[typeNames.Count];
        for (int i = 0; i < types.Length; i++)
        {
            try
            {
                types[i] = ReconstructType(typeNames[i], throwOnError);
                if (types[i] == null)  // if throwOnError, should not reach this point if couldn't create the type
                    return null;
            }
            catch (Exception ex)
            {
                throw new Exception($"Unable to reconstruct generic type. Failed on creating the type argument {(i + 1)}: {typeNames[i]}. Error message: {ex.Message}");
            }
        }

        Type resultType = genericType.MakeGenericType(types);
        return resultType;
    }

И вы можете проверить это с помощью этого кода (консольное приложение):

    static void Main(string[] args)
    {
        Type t1 = typeof(Task<Dictionary<int, Dictionary<string, int?>>>);
        string name = t1.AssemblyQualifiedName;
        Console.WriteLine("Type: " + name);
        // Result: System.Threading.Tasks.Task`1[[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
        Type t2 = ReconstructType(name);
        bool ok = t1 == t2;
        Console.WriteLine("\r\nLocal type test OK: " + ok);

        Assembly asmRef = Assembly.ReflectionOnlyLoad("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
        // Task<DialogResult> in refTypeTest below:
        string refTypeTest = "System.Threading.Tasks.Task`1[[System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
        Type t3 = ReconstructType(refTypeTest, true, asmRef);
        Console.WriteLine("External type test OK: " + (t3.AssemblyQualifiedName == refTypeTest));

        // Getting an external non-generic type directly from references:
        Type t4 = ReconstructType("System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", true, asmRef);

        Console.ReadLine();
    }

Я делюсь своим решением, чтобы помочь людям с той же проблемой, что и я (десериализовать ЛЮБОЙ тип из строки, которая может быть определена как частично или как целое в сборке с внешней ссылкой - и ссылки динамически добавляются пользователем приложения) ,

Надеюсь, это кому-нибудь поможет!

1 голос
/ 27 июня 2016

Сначала загрузите сборку, а затем тип. например: Сборка DLL = Assembly.LoadFile (PATH); DLL.GetType (TypeName);

1 голос
/ 14 мая 2016

Краткий и динамический подход с использованием свойства AssemblyQualifiedName -

Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)

Наслаждайтесь!

0 голосов
/ 18 августа 2010

Можете ли вы использовать один из стандартных способов?

typeof( MyClass );

MyClass c = new MyClass();
c.GetType();

Если нет, вам нужно будет добавить информацию о Type.GetType о сборке.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...