Загрузка универсального типа по имени, когда универсальные аргументы поступают из нескольких сборок - PullRequest
6 голосов
/ 17 февраля 2010

Мне нужно создать тип только из его полного имени, например: "System.String" или "Tuple'2 [string, Mytype]". в строке нет информации о сборке. Вот как выглядит код.

private static Type LoadType(string typeName)
{
    // try loading the type
    Type type = Type.GetType(typeName, false);

    if (type != null)
        return type;

    // if the loading was not successfull iterate all the referenced assemblies and try to load the type.
    Assembly asm = Assembly.GetEntryAssembly();
    AssemblyName[] referencedAssemblies = asm.GetReferencedAssemblies();
    foreach (AssemblyName referencedAssemblyName in referencedAssemblies)
    {
        type = referencedAssembly.GetType(typeName, false);
        if (type != null)
            return type;
    }
    throw new TypeLoadException(string.Format("Could not load the Type '{0}'",typeName));
}

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

Есть ли способ предоставить несколько сборок для разрешения типов при вызове GetTypes?

Ответы [ 4 ]

7 голосов
/ 17 февраля 2010

Тебе придется сделать это трудным путем, я думаю. К счастью, это не так сложно. Довольно просто:

  • Разобрать имя типа в определение типа и аргументы универсального типа.
  • Получить объект определения универсального типа
  • Получение объектов типа для каждого аргумента универсального типа
  • Создайте универсальный тип из определения универсального типа и аргументов универсального типа, используя метод MakeGenericType для объекта определения типа.
6 голосов
/ 17 февраля 2010

Как то так ....

Type.GetType("namespace.typename`1[[namespace.typename, assemblyname]], assemblyname");

, например

var type = Type.GetType("System.Collections.Generic.List`1[[System.String, mscorlib]], mscorlib");
var instance = Activator.CreateInstance(type);

или, как говорит Эрик .. если у вас в руках есть типы, просто создайте их ..

Type genericType = typeof(Dictionary<,>);
Type constructedType = genericType.MakeGenericType(new Type[] { typeof(String), typeof(String) });
2 голосов
/ 17 декабря 2013

Вот мой сложный способ получить любой тип:

    /// <summary>
    /// Gets the type associated with the specified name.
    /// </summary>
    /// <param name="typeName">Full name of the type.</param>
    /// <param name="type">The type.</param>
    /// <param name="customAssemblies">Additional loaded assemblies (optional).</param>
    /// <returns>Returns <c>true</c> if the type was found; otherwise <c>false</c>.</returns>
    public static bool TryGetTypeByName(string typeName, out Type type, params Assembly[] customAssemblies)
    {
        if (typeName.Contains("Version=") 
            && !typeName.Contains("`"))
        {
            // remove full qualified assembly type name
            typeName = typeName.Substring(0, typeName.IndexOf(','));
        }

        type = Type.GetType(typeName);

        if (type == null)
        {
            type = GetTypeFromAssemblies(typeName, customAssemblies);
        }

        // try get generic types
        if (type == null
            && typeName.Contains("`"))
        {
            var match = Regex.Match(typeName, "(?<MainType>.+`(?<ParamCount>[0-9]+))\\[(?<Types>.*)\\]");

            if (match.Success)
            {
                int genericParameterCount = int.Parse(match.Groups["ParamCount"].Value);
                string genericDef = match.Groups["Types"].Value;
                List<string> typeArgs = new List<string>(genericParameterCount);
                foreach (Match typeArgMatch in Regex.Matches(genericDef, "\\[(?<Type>.*?)\\],?"))
                {
                    if (typeArgMatch.Success)
                    {
                        typeArgs.Add(typeArgMatch.Groups["Type"].Value.Trim());
                    }
                }

                Type[] genericArgumentTypes = new Type[typeArgs.Count];
                for (int genTypeIndex = 0; genTypeIndex < typeArgs.Count; genTypeIndex++)
                {
                    Type genericType;
                    if (TryGetTypeByName(typeArgs[genTypeIndex], out genericType, customAssemblies))
                    {
                        genericArgumentTypes[genTypeIndex] = genericType;
                    }
                    else
                    {
                        // cant find generic type
                        return false;
                    }
                }

                string genericTypeString = match.Groups["MainType"].Value;
                Type genericMainType;
                if (TryGetTypeByName(genericTypeString, out genericMainType))
                {
                    // make generic type
                    type = genericMainType.MakeGenericType(genericArgumentTypes);
                }
            }
        }

        return type != null;
    }

    private static Type GetTypeFromAssemblies(string typeName, params Assembly[] customAssemblies)
    {
        Type type = null;

        if (customAssemblies != null
           && customAssemblies.Length > 0)
        {
            foreach (var assembly in customAssemblies)
            {
                type = assembly.GetType(typeName);

                if (type != null)
                    return type;
            }
        }

        var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
        foreach (var assembly in loadedAssemblies)
        {
            type = assembly.GetType(typeName);

            if (type != null)
                return type;
        }          

        return type;
    }
2 голосов
/ 18 апреля 2013

Если формат ´t1 [[t2, a2] [t3, a3]], a1´, это работает:

private Type GetAckwardType(string typeName)
{
    var tokens = typeName.Split(new []  {"[[", "]]", "]["}, StringSplitOptions.None);
    if (tokens.Length == 1)
        return Type.GetType(typeName, true);

    var plainType = Type.GetType(tokens[0] + tokens[tokens.Length - 1], true);
    var args = tokens.Skip(1).Take(tokens.Length - 2).Select(_ => Type.GetType(_, true)).ToArray();
    return plainType.MakeGenericType(args);
}
...