.NET 反射问题

6
以下代码(打包在“Console Application”Visual Studio项目中):
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace TestReflection
{
    class Program
    {
        static void Main(string[] args)
        {
            bool found = false;
            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assembly.GetType("System.Diagnostics.Process") != null)
                {
                    found = true;
                    break;
                }
            }
            Console.WriteLine(found);
            Console.ReadKey();
        }
    }
}

在调试模式下运行(按F5键)时会打印'True',但在没有调试器的情况下启动(按Ctrl-F5键)时会打印'False'。其他类也表现出类似的行为(System.Text.RegularExpressions.Regex),而其他类则在两种情况下都存在(System.IO.File)。

我可能忽略了一些显而易见的东西 - 这是为什么?

(在Visual Studio 2005和2008中均发生相同的情况)。

更新

找到的程序集列表:

调试模式:

mscorlib
TestReflection
System
Microsoft.VisualStudio.HostingProcess.Utilities
System.Windows.Forms

运行模式:

mscorlib
TestReflection

如答案所示,在运行模式下,系统装配件丢失(未加载)。我的问题是我以为 GetAssemblies() 也返回了未加载的程序集。

虽然这解释了 System.Diagnostics.Process 的行为,但为什么我的代码在运行和调试模式下都能找到 System.IO.File 呢?

谢谢!

更新2

我已经更改了代码,通过循环遍历已加载的程序集和当前程序集,收集这些程序集引用的程序集的列表。如果在迭代加载的程序集后找不到我要查找的类型,则开始加载并检查引用的程序集。

似乎即使我在项目中对 System.dll 进行了引用(我正在查看 Visual Studio 中“System”引用的属性),我的可执行文件只引用 mscorlib.dll(根据 Reflector 中的显示)。

如何添加对 System.dll 的“真实”引用?放置一个虚拟行

new System.Diagnostics.ProcessStartInfo();

Main的开头就能解决这个问题(在检查可执行文件时,Reflector显示了对mscorlib.dllSystem.dll的引用),但这是一种hack方法。
再次感谢!

你能否添加一下在调试器中和不在调试器中获取的程序集列表? - Peter Lillevold
1
如果您需要强制将所有引用的程序集加载到内存中,可以遍历每个已加载的程序集并调用GetReferencedAssemblies()方法,该方法返回一个AssemblyName实例数组。然后对于这些实例,您可以使用Assembly.Load来强制加载它们。但是要注意不要对所有系统程序集都这样做。同时,请注意,仅因为您在项目中引用了一个程序集,并不意味着它从.NET的角度来看就是一个“引用的程序集” - 如果没有使用该引用的代码,则它会在最终的二进制文件中被剥离。 - Andras Zoltan
我已经更新了我的答案以回答你的附加问题 :) - Peter Lillevold
嗯,我正在玩一个玩具语言的解释器,我想让.NET函数对该语言可用。这是一个非常简单的外部函数接口(FFI)。例如,如果用户输入DateTime.Now,我希望能够查找DateTime类,然后在DateTime中查找名为“Now”的公共方法/属性/字段。 - Miron Brezuleanu
在这种情况下(是的,这实际上应该是第二个问题:),为什么不在解释器/运行时中拥有已知程序集列表呢?每当首次引用类型(例如您示例中的DateTime)时,您可以查询已知程序集列表以找到支持该类型的程序集…… - Peter Lillevold
显示剩余3条评论
5个回答

11

AppDomain.GetAssemblies 方法只会返回已经被加载到应用程序域中的程序集,而不是你所引用的所有程序集。

很明显,Diagnostics.Process 类并未在你的应用程序中直接使用,因此在没有调试器的情况下运行时也不会被加载。

那么为什么我们能找到 System.IO.File 而不能找到 System.Diagnostics.Process 呢? 原因是这两个类虽然位于同一个顶层命名空间 System 中,但实际上它们分别存在于两个不同的程序集中。通过查看 Visual Studio 对象浏览器中的这两个类,可以很容易地看出来。File 类恰好位于 mscorlib.dll 中,而 Process 类位于 System.dll 中。

由于没有任何一个类型的引用位于 System.dll 中,因此该程序集不会被加载,而 .Net 应用程序无法在没有 mscorlib 程序集的情况下运行,因此该程序集将被加载。


程序集会在第一次使用或者引用程序集中的任何内容时被加载。 - Julien Roncaglia

4
在VS中的调试器会尽其所能欺骗您,让您认为您正在运行应用程序,就像在生产环境中一样。但是,它会做很多意想不到的事情,例如急切地加载程序集,使变量长时间存活,等等。
在发布版本中,CLR不会在需要执行代码时将程序集加载到应用程序域中。但是,在调试期间无法保证此行为。
如果您的代码对这些更改敏感,建议重新设计代码或检查System.Diagnostics.Debugger.IsAttached

0

你可能会发现,使用调试器和不使用调试器执行这行代码会有趣:

Console.WriteLine(Process.GetCurrentProcess().ProcessName);

简而言之,正如@Will所指出的那样,当您在VS中附加调试器运行时,实际上并没有运行“您的exe”,而是VS调试器为您设置的整个框架。事情将会有所不同。

0

我想象一些程序集并不是直接被你的项目引用的,但在调试时会被调试器本身映射到你的进程中。

确保你要查找的程序集在你的项目的引用中。


这也是我想的,但是 System.Diagnostics.Process 应该在 System.dll 中。 :-) - Miron Brezuleanu

0
通常情况下,只有在需要时才会加载程序集。在调试模式下运行时,调试器会在启动时加载Microsoft.VisualStudio.HostingProcess.Utilities程序集。我猜测这个程序集会调用System程序集,因此在调试模式下启动时会加载System程序集。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接