Ignoring Version in an assembly-qualified name passed to Type.GetType()
Asked Answered
S

5

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.

Schuler answered 21/7, 2009 at 13:55 Comment(1)
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.Hayfork
S
1

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.

Schuler answered 21/7, 2009 at 15:44 Comment(3)
I do not have a strongly named application, and this is definitely not the case.Lodovico
It depends on .net version e.g. 4.7.2 vs 5.0Organism
In .net6.0, it works only if you request a lower version, not an higher one.Sapotaceous
B
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);
}
Benedix answered 22/10, 2015 at 8:56 Comment(1)
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 learn.microsoft.com/en-us/dotnet/api/…Jeramyjerba
S
1

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.

Schuler answered 21/7, 2009 at 15:44 Comment(3)
I do not have a strongly named application, and this is definitely not the case.Lodovico
It depends on .net version e.g. 4.7.2 vs 5.0Organism
In .net6.0, it works only if you request a lower version, not an higher one.Sapotaceous
Z
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+.

Zhang answered 19/1, 2022 at 14:16 Comment(0)
A
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}";
        }
    }
}

Apathetic answered 9/5, 2022 at 17:53 Comment(0)
P
0

Expanding on answer by @PJC I got following TypeResolver:

public static class TypeResolver
{
    private static Dictionary<string, Type> _typesCache = new();

    public static Type? TryGetType(string typeName)
    {
        if (_typesCache.TryGetValue(typeName, out var cachedType))
        {
            return cachedType;
        }

        var type = Type.GetType(typeName);
        if (type == null)
        {
            type = Type.GetType(typeName, AssemblyResolver, null);
            if (type != null)
            {
                _typesCache[typeName] = type;
            }
        }

        return type;
    }
    
    private static System.Reflection.Assembly AssemblyResolver(System.Reflection.AssemblyName assemblyName)
    {
        assemblyName.Version = null;
        return System.Reflection.Assembly.Load(assemblyName);
    }
}

The original answer did not work for me exactly, because my use-case is messaging library.

And for messaging - it needs to be more robust, I suspect the Assembly.Load will be quite a bottleneck, especially when there are loads of messages per second.

I think caching is a way to go.

The accepted answer did not work for me, because as correctly mentioned by @Dunge in new .NET the Type.GetType() method only works if the given type's version is lower than current assembly's version, if it's higher it'll return null.

Ponzo answered 26/4, 2024 at 17:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.