14

Is it possible to get a Type via Type.GetType() when the assembly-qualified name passed into GetType() specifies a different Version than the version of the DLL that's actually loaded? If so, what is the behavior of GetType()?

I want to get a Type from an assembly regardless of what version the assembly is. I have a function which gets an assembly-qualified name as an argument:

Type someType = Type.GetType(someName);

The someName value corresponds to the Type I want to get, but it may not have the same Version specified as what is loaded in my application.

sourcenouveau
  • 29,356
  • 35
  • 146
  • 243
  • This question is not precise enough...what do you mean by wanting to get a type from an assembly, but not knowing which type you want to get fromw which assembly? That does not sound very logical. And the whole purpose of "assembly-qualified names" is to define verion and token with the name. Please edit the question to clarify. – galaktor Jul 21 '09 at 14:05

4 Answers4

20

I've used this successfully:

Type type = Type.GetType(typeName, AssemblyResolver, null);

private static System.Reflection.Assembly AssemblyResolver(System.Reflection.AssemblyName assemblyName)
{
    assemblyName.Version = null;
    return System.Reflection.Assembly.Load(assemblyName);
}
PJC
  • 201
  • 2
  • 3
  • This worked for me. FYI at least in .NET 7 if you do not have a full assembly name in the string it will not call the resolver. See https://learn.microsoft.com/en-us/dotnet/api/system.type.gettype?view=net-7.0#system-type-gettype(system-string-system-func((system-reflection-assemblyname-system-reflection-assembly))-system-func((system-reflection-assembly-system-string-system-boolean-system-type))) – Rob Baily Jun 05 '23 at 14:36
2

In testing I found that GetType() will return the proper type even if the currently-loaded assembly's version does not match the value in the assembly-qualified name's Version field.

sourcenouveau
  • 29,356
  • 35
  • 146
  • 243
0

Another possibilty: shorten the type name to its FullName and AssemblyName. When "serializing" use:

public static string GetShortTypeName(this Type type)
{
  return $"{type.FullName}, {type.Assembly.GetName().Name}";
}

or before "deserializing":

public static string ShortenTypeName(string assemblyQualifiedName)
{
  var cPos1 = assemblyQualifiedName.IndexOf(',');
  if (cPos1 < 0 || cPos1 == assemblyQualifiedName.Length - 1)
    return assemblyQualifiedName;

  var cPos2 = assemblyQualifiedName.IndexOf(',', cPos1 + 1);
  if (cPos2 < 0)
    return assemblyQualifiedName;

  return assemblyQualifiedName.Substring(0, cPos2);
}

In this case the assemblyName.Version in the AssemblyResolver in @PJC answer is always null so the custom resolver is not needed anymore.

This works in .Net Framework and .NET Core/.NET 5+.

Lars
  • 6,421
  • 1
  • 23
  • 24
0

In later versions (insert version number here) of the framework, using GetType() and giving it a type string with the wrong version number will still give you the correct type instead of null.

If you write a library that can be used by applications with a previous version (insert version number here), you can instead give to the GetType() Method a string that does not include the version instead.

Remember that asking the fullName of a generic type will give you a string including the versions of the generic type parameters, you will need to write the type string yourself. You can use my code for this, it uses @Lars's code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace hlwSerial
{
    public static class TypeHelper
    {
        /// <summary>
        /// Gives a shortened assemblyQualifiedName of the type, with only its fullName and the name of its assembly. Does the same for its generic type parameters if it has any.
        /// </summary>
        /// <param name="type">The type of which you'll get the name</param>
        /// <param name="inBrackets">default to false. Put true if the result should be surrounded by brackets in the case of being a generic type parameter.You shouldn't have to set it to true yourself.</param>
        /// <returns></returns>
        public static string GetShortTypeName(this Type type, bool inBrackets = false)
        {
            if (type.IsGenericType) return type.GetShortGenericName(inBrackets);
            if (inBrackets) return $"[{type.FullName}, {type.Assembly.GetName().Name}]";
            return $"{type.FullName}, {type.Assembly.GetName().Name}";
        }

        /// <summary>
        /// Private function that will be called by the GetShortTypeName method if the type tested is generic.
        /// </summary>
        /// <param name="type">The type of which you'll get the name</param>
        /// <param name="inBrackets">default to false. Put true if the result should be surrounded by brackets in the case of being a generic type parameter. You shouldn't have to use this.</param>
        /// <returns></returns>
        private static string GetShortGenericName(this Type type, bool inBrackets = false)
        {
            if (inBrackets)
                return $"[{type.GetGenericTypeDefinition().FullName}[{string.Join(", ", type.GenericTypeArguments.Select(a => a.GetShortTypeName(true)))}], {type.Assembly.GetName().Name}]";
            else
                return $"{type.GetGenericTypeDefinition().FullName}[{string.Join(", ",type.GenericTypeArguments.Select(a=> a.GetShortTypeName(true)))}], {type.Assembly.GetName().Name}";
        }
    }
}

holeo hlw
  • 13
  • 4