【问题标题】:Getting the System.Type from an assembly loaded at runtime从运行时加载的程序集中获取 System.Type
【发布时间】:2014-04-27 14:39:40
【问题描述】:

作为this question 的后续行动,我现在遇到的问题是能够获得用户在自己的解决方案中定义的类型的Type。使用标准的 mscorlib 类型,一切正常。

这个问题很简单:我怎样才能从我只会在运行时知道的程序集中获得这种类型?

the comments here中所述:

另外,“提取类型”是什么意思?你的意思是得到反射类型?没有好帮手,部分原因是(通常)你永远不能假设你正在编译的类型在运行时是可创建的。 “编译时”和“运行时”之间有很强的(但经常被忽视的)区别,很少能将它们联系起来。

或者这里关于上一个问题:

好吧,所以为TypeInfo 获得一个Type,除了命名问题之外,这是一个棘手的问题。它假定您有一个可以加载和找到的程序集。当您进行构建时,编译器可能正在加载本身不能作为“正常”程序集加载的引用程序集。即使它们是,您也可能需要挂钩 AppDomain.AssemblyResolve 来定位您的引用以及您构建的任何程序集。

“构建”和“运行时”是真正不同的领域,从一个到另一个的交叉定义不明确,充其量。我在这里假设您确实需要 System.Type,因为您正在使用其他一些反射 API,或者尝试加载该类型并从中执行代码。

我遵循了here 中的方法并在我的分析器中实现了它:

private static Dictionary<string, Assembly> _assemblies = new Dictionary<string, Assembly>();

var containingAssembly = semanticModel.GetSymbolInfo(argument)
                                      .Symbol
                                      .ContainingAssembly;

if (!_assemblies.TryGetValue(containingAssembly.ToString(), out Assembly result))
{
    var newAssembly = Assembly.Load(containingAssembly.ToString());
    _assemblies.Add(containingAssembly.ToString(), newAssembly);
}

var currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += ResolveAssemblies;

private Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
    _assemblies.TryGetValue(args.Name, out Assembly result);
    return result;
}

但这并没有什么不同,我不断得到

用户诊断分析器“DiagnosticTools.Collections.ElementaryMethodsNotOverriden.ElementaryMethodsNotOverridenAnalyzer”引发异常,并显示消息“无法加载文件或程序集“RoslynTester,版本=1.0.0.0,Culture=neutral,PublicKeyToken=null”或其依赖项之一.系统找不到指定的文件。'。

使用fuslogvw.exe 给我this log information 归结为

LOG:所有探测 URL 都已尝试并失败。

/Common7/IDE/ 的几个子文件夹中搜索.dll.exe 版本后。

作为澄清我为什么这样做的上下文:我想检查集合中使用的每个类型并验证它是否覆盖了EqualsGetHashCode。为了确定这一点,我有一个“经典”反射扩展方法可以帮我检查:

public static bool IsOverridden(this MethodInfo method)
{
    return method.GetBaseDefinition().DeclaringType != method.DeclaringType;
}

如果 Roslyn 有办法自己验证这一点,这样我就不必使用经典反射,那么这也可以。

更新:

当我使用此代码 as provided by MSDN 时,我在 Visual Studio 中收到“无效参数”异常,但 fuslogvw 仍然显示“找不到文件”错误消息。造成这种差异的原因是什么?

private Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
    Assembly MyAssembly, objExecutingAssemblies;
    string strTempAssmbPath = "";
    
    objExecutingAssemblies = Assembly.GetExecutingAssembly();
    AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
    
    foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
    {
        if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
        {               
            strTempAssmbPath = @"C:\Users\Jeroen\Documents\Visual Studio 2013\Projects\RoslynTester\RoslynTester\bin\Debug\" + args.Name.Substring(0, args.Name.IndexOf(",")) + ".exe";
            break;
        }
    }                   
    MyAssembly = Assembly.LoadFrom(strTempAssmbPath);
    
    return MyAssembly;
}

【问题讨论】:

  • 这是一个“找不到文件”错误,这是最常见的错误。非常不清楚为什么您认为 CLR 应该能够找到该文件。如果它不在 GAC 中并且不在探测路径中,那么这就是它的结束,Fuslogvw 向您展示了它的外观。您的 AssemblyResolve 事件不会完成任何事情,它只能找到之前已加载的程序集,并且 CLR 已经知道如何自行完成。
  • 我已经研究了这些概念,但还有一些问题。我认为 GAC 不是一个解决方案,因为它会迫使用户手动将他的程序集添加到其中。此外:RoslynTester 项目只是一个控制台应用程序,诊断信息(依赖于从该程序集获取类型)应该在构建项目之前可用。当我在编译时不知道在哪个目录中找到新程序集时,如何将ResolveAssemblies 指向新程序集?我已经更新了ResolveAssemblies 代码。

标签: c# .net reflection .net-assembly roslyn


【解决方案1】:

我假设您已经(以编程方式)发现了您的集合中包含哪些类。你真的不需要反思来完成你想要的。使用 Roslyn,您可以检查一个类是否使用此 SyntaxWalker 覆盖了 Equals 方法:

public class FindOverrides : CSharpSyntaxWalker
{
    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        base.VisitMethodDeclaration(node);

        if (node.Identifier.Text == "Equals" 
            && node.Modifiers.Any(m => m.Text == "override"))
        {
            // found an override of Equals()    
        }
    }
}

要(盲目地)检查给定解决方案中每种类型的每种方法,可以这样使用:

var syntaxRoots =
    from project in solution.Projects
    from document in project.Documents
    select document.GetSyntaxRootAsync().Result;

foreach (var root in syntaxRoots)
    new FindOverrides().Visit(root);

至少有一个遗漏(如果这对您来说是一个有效的情况):我上面的代码不会找到给定类型的基类是否覆盖Equals()

【讨论】:

  • 这种方法看起来很有前途,谢谢你指点我!在implementing your code 之后,我得到One or more errors occurred 异常,当我打开AggregateException 时,我看到堆栈跟踪深入System.Threading。我是否在我的实现中忽略了一些可能导致这种情况的东西?
  • 嗨,很高兴我能帮上忙。通过阅读您的要点,我无法立即发现问题。请查看您遇到的AggregateException 中的InnerException(s)。如果这不能帮助您修复它,请将异常连同堆栈跟踪和导致异常的(最少)一段代码一起发布。
  • 我设法解决了它(虽然我忘记了它到底是什么,但我的代码从那以后经历了 3 次重构)。我接受了 SyntaxWalker 的想法,但最终发现它不是必需的:TypeInfo.Type.GetMembers() 包含在该级别声明的所有成员(以及方法),并具有显式的 .IsOverride 属性。所以我几乎扔掉了一大堆代码,并用这几行代码替换了它。从最初的检查来看,它似乎工作得很好,明天我将在有机会查看它之后添加一个 CW 答案。感谢您的意见!
猜你喜欢
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
  • 1970-01-01
  • 2018-11-08
  • 1970-01-01
  • 2010-10-05
  • 2011-09-09
相关资源
最近更新 更多