忽略传递给 Type.GetType() 的程序集限定名称中的版本

问题描述 投票:0回答:5

当传递到

Type
的程序集限定名称指定与实际加载的 DLL 版本不同的
Type.GetType()
时,是否可以通过
GetType()
获取
Version
?如果是这样,
GetType()
的行为是什么?

我想从程序集中获取

Type
,无论程序集是什么版本。我有一个函数,它获取程序集限定名称作为参数:

Type someType = Type.GetType(someName);

someName
值对应于我想要获取的
Type
,但它可能与我的应用程序中加载的内容指定的
Version
不同。

.net reflection types version
5个回答
20
投票

我已经成功使用了这个:

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

1
投票

在测试中我发现,即使当前加载的程序集的版本与程序集限定名称的

GetType()
字段中的值不匹配,
Version
也会返回正确的类型。


0
投票

另一种可能性:将类型名称缩短为其

FullName
AssemblyName
。当“序列化”时使用:

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

或在“反序列化”之前:

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

在这种情况下,@PJC 答案中的

assemblyName.Version
中的
AssemblyResolver
始终为
null
,因此不再需要自定义解析器。

这适用于 .Net Framework 和 .NET Core/.NET 5+。


0
投票

在框架的更高版本(在此处插入版本号)中,使用

GetType()
并给它一个带有错误版本号的类型字符串仍然会为您提供正确的类型而不是 null。

如果您编写的库可供具有先前版本的应用程序使用(在此处插入版本号),您可以改为向

GetType()
方法提供一个不包含版本的字符串。

请记住,询问泛型类型的

fullName
将为您提供一个包含泛型类型参数版本的字符串,您需要自己编写类型字符串。你可以使用我的代码,它使用@Lars的代码:

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


0
投票

扩展 @PJC 的 answer,我得到以下内容

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.Add(typeName, type);
            }
        }

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

最初的答案并不完全适合我,因为我的用例是消息传递库。

对于消息传递 - 它需要更加健壮,我怀疑

Assembly.Load
将是一个相当大的瓶颈,特别是当每秒有大量消息并且我们正在每一条消息上加载程序集时。

我认为缓存是一种可行的方法。

接受的答案对我不起作用,因为正如 @Dunge in new .NET 正确提到的

Type.GetType()
方法仅在给定类型的版本低于当前程序集的版本时才有效,如果更高,它将返回 null .

© www.soinside.com 2019 - 2024. All rights reserved.