6

I need to create a type from its full name only Ex: "System.String" or "Tuple'2[string,Mytype]". there is no information about the assembly in the string. Here is what the code look like.

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

this method works when the type is not generic. But for generic types iterating through the assemblies always fails because no assemblies contains all the definitions required to build the type.

Is there a way to provide multiples assemblies for type resolution when calling GetTypes ?

starblue
  • 55,348
  • 14
  • 97
  • 151
Cheick
  • 2,154
  • 24
  • 28
  • if you are programmatically pulling the typenames and munging the generic type name to be created, for a List for instance, be sure to use the AssemblyQualifiedName. – Sky Sanders Feb 16 '10 at 21:26
  • 1
    What are you going to do about the case where there are two types in two different referenced that have exactly the same name? – Eric Lippert Feb 16 '10 at 22:08
  • all types are prefixed with their namespace so hopefully this will not happened often, the subsequent deserialization will fail. I can backtrack and try another type in another assembly and retry the deserialization.But i know it is easier said than done. – Cheick Feb 17 '10 at 14:05
  • @EricLippert Can you help me understand why disambiguation is enforced only for generic types? For example, `typeof(MyType).FullName` will not return the assembly information, whereas `typeof(List).FullName` will. In the former case, the issue you raise in your comment seems to be "by-design". The solution for "consistent" behaviour seems to be [using `ToString()` instead of `FullName`](https://stackoverflow.com/questions/2132729/fullname-of-generic-type-without-assembly-info), but I find that to be a bit hackish. What led to this design decision? – Lazlo May 25 '17 at 16:06
  • 1
    @Lazlo: Until you pointed this out just now, I had never considered to ask why `FullName` is inconsistent like that. I would expect that `FullName` would recursively use the `FullName` of every constituent type in a generic type, not the assembly-qualified name. I have no idea why this design decision was made. Maybe check the source code and see if there is an illuminating comment. – Eric Lippert May 25 '17 at 16:18
  • It is unfortunate, because it causes inconsistent behaviour when serializing types by `FullName`, and trying to achieve consistency by serializing them with `ToString()` makes assembly lookup fail for generic types. – Lazlo May 25 '17 at 16:22

4 Answers4

8

You're going to have to do it the hard way I think. Fortunately it's not that hard. Pretty straightforward:

  • Parse the type name into the type definition and the generic type arguments.
  • Obtain the generic type definition object
  • Obtain the type objects for each generic type argument
  • Construct the generic type out of the generic type definition and the generic type arguments using the MakeGenericType method on the type definition object.
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I thought about this solution but it's the last step i find difficult, let's say i parsed Tuple'2[System.String,MyNameSpace.MyType] and i'm able to get the three diffrents types (Tuple,String and MyType). How do i create the instance t of Type such as : t == typeof(Tuple) – Cheick Feb 16 '10 at 21:33
  • 4
    Type genericType = typeof(Dictionary<,>); Type constructedType = genericType.MakeGenericType(new Type[] { typeof(String), typeof(String) }); – Sky Sanders Feb 16 '10 at 21:43
  • "Straightforward" until you take recursion into account. ;) – Lazlo May 25 '17 at 16:14
7

Something like this....

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

e.g.

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

or, as Eric says.. if you have the types in hand, just build it..

Type genericType = typeof(Dictionary<,>);
Type constructedType = genericType.MakeGenericType(new Type[] { typeof(String), typeof(String) });
Sky Sanders
  • 36,396
  • 8
  • 69
  • 90
  • well then, unless the assembly is loaded or you can load it AND there are no ambiguous matches, you are out of luck. sorry. – Sky Sanders Feb 16 '10 at 21:27
  • is it possible to force the loading of the assemblies ? – Cheick Feb 16 '10 at 21:41
  • any one of the Assembly.Load methods. If you have to load the assemblies then you will have to munge up a string typename and create it. If you have the types already loaded, e.g. using xxx.xx, you can just build it using Type methods as shown and described by Eric – Sky Sanders Feb 16 '10 at 21:47
4

Here is my hard way solution to get any type:

    /// <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;
    }
Benni
  • 203
  • 2
  • 7
3

If the format is ´t1[[t2, a2][t3, a3]], a1´, this works:

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);
}
Lars Corneliussen
  • 2,513
  • 2
  • 26
  • 36