为什么加载包含Value-Type字段的类会强制CLR加载该值类型?

时间:2014-04-13 16:21:01

标签: c# .net clr .net-assembly value-type

假设我在以下程序集中有以下类型:

Assembly1:

public struct DependencyStruct { }
public class DependencyClass { }

Assembly2:

public class UsingDependency
{
    private DependencyStruct m_DependencyStruct; // having this will field will cause the loading of the DependencyStruct type (thus will cause an assembly binding request).
    private DependencyClass m_DependencyClass; // having this field will **not** cause the loading of the DependencyClass type.
}

Assembly3(可执行文件)

public class Program
{
    static void Main(string[] args)
    {
       Assembly assembly = Assembly.LoadFrom("Assembly2.dll");
       Type[] types = assembly.GetTypes();
       Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
    }
}

当我运行以下代码时,我会在程序集数组中找到Assembly2和Assembly1。

如果我注释掉m_DependencyStruct声明,我将在程序集数组中找到只有Assembly2。

请注意,我没有在此代码中创建任何实例,只是加载类型。

我的问题是:

  1. 为什么有值类型字段会导致CLR加载整个类型(而不是具有延迟加载的引用类型)?

  2. 有没有办法推迟这种值类型加载?使用Lazy<DependencyStruct> m_LazyDependencyStruct或创建另一个包装器类会起作用,但我很好奇是否有另一种方法可以在不改变实际类型的情况下这样做。

  3. 谢谢!

4 个答案:

答案 0 :(得分:3)

这很难确定,加载类型的CLR中的代码是一个复杂的C ++代码。我在MethodTableBuilder :: InitializeFieldDescs()方法中看到的一件事是,它还计算了类中字段的偏移量。

这需要知道每个字段需要多少存储空间。对于引用类型的字段来说,这当然很简单,它只是指针的大小。但不是对于值类型的字段,它需要加载字段的类型,必要时通过其字段递归来计算它们的大小。当然,您会看到包含加载的值类型的程序集的副作用。

只是一个有根据的猜测。您可以查看SSCLI20发行版中的class.cpp源代码文件以查找自己。强大的实施细节,确保您从不关心这一点。

答案 1 :(得分:1)

这是因为struct变量的声明导致结构被分配,因为struct不能为null,因此变量是struct的默认版本。

类变量的声明可以为null,因此不需要分配类的出现次数。

答案 2 :(得分:0)

由于类(引用类型)的默认值为null,因此不需要加载类型来设置其默认值,而在struct作为值类型时,它应该初始化为new StructType这是默认值,因此您会看到此行为。

答案 3 :(得分:0)

为了创建包含值类型字段的类型的实例,CLR需要知道在类的数据部分中分配多少空间。值类型的整个目的是它们以“按值”传递 - 整个类型存在并直接访问。这意味着该字段的默认值需要将每个成员初始化为默认值等。这样做需要加载该类型的所有元数据。

另一方面,引用类型是“通过引用”传递的 - 您的容器类唯一需要知道的是引用的大小,对于所有引用类型都是相同的。在您实际尝试使用它之前,CLR不需要了解有关引用类型的任何信息。引用类型的默认值不会“使用”特定于该类型的任何内容,因此无需在以后加载元数据。