一校 疑难汇总

时间:2021-03-19 09:46:12

第6章 模块和程序集

1.这一整段,请帮我确认一下

In this regard, the IL assembler differs from other managed compilers (VB, C#, VC++),
as those compilers require the specification of referenced assemblies via the file path instead
of querying the GAC. This might play a bad trick on a programmer, because the CLR loader
always tries to load the assemblies from the GAC first (as is described in the next section), and
in the unlikely event of a mismatch between referenced assemblies installed in the GAC and
those specified by the file path, the application will be executed against assemblies different
from those it was built against.

在这点上,IL汇编器与其它托管的编译器(VBC#VC++)有所不同,就好像那些编译器需要的是被引用程序集的说明,它们使用文件路径而不是搜索GAC。这可能是在捉弄程序员,因为CLR加载程序总是试图首先从GAC加载程序集(下一节会讨论),并且万一出现安装在GAC中被引用的程序集和在文件路径中指定的程序集不匹配的情况,那么应用程序在执行时所依赖的程序集将不同于它被创建时所依赖的程序集。


第7章 命名可空间和类

2.粗体字部分,有问题

Can value types have virtual methods? Yes, they can. However, to call the virtual methods
of a value type, you have to box this value type first. I must clarify, though, that you need to
box the value type only if you are calling its virtual method as a virtual method, through the
virtual table dispatch, using the callvirt instruction (methods and method call instructions
are discussed in Chapters 10, 12, and 13).
If you are calling a virtual method of a value type as
simply an instance method, using the call instruction, you don’t need to box the value type.
That’s why I didn’t need to box the variable J in the previous code snippet before calling the
ToString() method despite that ToString() is a virtual method.

值类型可以有虚方法么?是的,可以。然而,为了调用值类型的虚方法,必须首先对值类型装箱。可是,必须澄清,只有在调用值类型的虚方法就像使用callvirt指令、通过虚拟表分配来调用一个虚方法的时候,才需要对值类型装箱(方法和方法调用指令在第101213章讨论)。如果调用值类型的虚方法就像使用call指令调用实例方法那样简单,那么就不需要对值类型装箱。这是我为什么在前面的代码段中,在调用ToString()之前不需要对J进行装箱的原因,虽然ToString()是一个虚方法。

 

3.粗体字部分,有问题

Generally speaking, you can think of an enumeration as a restriction of its underlying
type to a predefined, finite set of values
(however, the CLR does not enforce this restriction).

一般来说,可以把枚举看作是在预定义的、有限的数值集合上对其内在类型的一种约束(然而,CLR并不会强制这种约束)。

 

4.这一整段,请帮我确认一下

This method of class “bookkeeping” messes up royally the order of class declaration on
round-tripping (disassembling and reassembling of a module), because the classes in the
round-tripped module are emitted not in the order they were emitted in the original module,
but rather in the order they were mentioned in the disassembly. This is a minor issue, because
the order of class definitions (TypeDef records) does not really matter, except in the case of
nested classes (enclosing class must be declared before the nested class), and this case is han-
dled properly by the IL assembler.

这种对类进行“记账”的方法在双向解析(反汇编和汇编一个模块)时会搅乱类声明的原先顺序,因为在被双向解析的模块中,类的流出顺序不是按照这些类在原始的模块中流出的顺序,而是按照它们在反汇编中的出现顺序。这是一个小问题,因为类的定义(TypeDef纪录)的顺序并不受影响,但是嵌套类除外(必须在嵌套类之前声明外包类),而IL汇编器已经妥善地处理了后面这种情况。

 

第8章 基本类型和签名

5.粗体字部分,有问题

Although the modreq and modopt modifiers have no effect on the managed types of the items
to which they are attached, signatures with and without these modifiers are considered different.
The same is true for signatures differing only in classes referenced by these modifiers. This
allows, for example, the overloading of functions having arguments of type int and long.

  虽然修饰符modreqmodopt对它们所修饰项的托管类型没有任何影响,但是,带有修饰符的签名和不带修饰符的签名是不同的。由这些修饰符所引用的类的签名也是不同的。例如,允许具有intlong类型参数的方法重载。

 

6.粗体字部分,有问题

The pinned modifier is applicable to the method’s local variables only. Its use means that
the object referenced by the local variable cannot be relocated by the garbage collector and
must stay put throughout the method execution. If a local variable representing an object ref-
erence or a managed pointer is “pinned,” it is safe to convert it to an unmanaged pointer and
then to dereference this unmanaged pointer, because the unmanaged pointer is guaranteed to
still be valid when it is dereferenced (it is safe in the sense of dereferencing, but it is still unver-
ifiable, as is any usage of an unmanaged pointer
):

修饰符pinned只适用于方法的局部变量。使用它就意味着由局部变量引用的对象不可以被垃圾收集器重新分配,并且在方法执行期间必须保持不动。如果一个表示对象引用或托管指针的局部变量是“pinned”,那么将其转换为非托管指针就是安全的,解除这个非托管指针的引用也是安全的,因为在解除对它的引用时,仍然能确保这个指针是有效的。(虽然它在解除引用的情况下是安全的,但它仍然是不可验证的,和非托管指针的所有用法一样):

 

7.一句话,还有Effectively这个词的翻译

To call methods indirectly, IL has the special instruction calli. This instruction takes argument
values plus a function pointer from the stack and uses the StandAloneSig token as a parameter.
The signature indexed by the token is the signature by which the call is made. Effectively,
calli takes a function pointer and a signature and presumes that the signature is the correct one to
use in calling this function:

IL有特殊指令calli来间接调用方法。这个指令从栈上得到参数值和函数指针,并使用StandAloneSig标记作为参数。就是由这个标记索引的签名,执行了调用。实际上,calli会得到函数指针和签名,并假定这个签名就是在调用以下函数中要使用的签名。

 

8.parented前后的逻辑

In the newobj instruction, we specified a MemberRef of the constructor method, parented
not by a type but by a constructed type, int32[0...,0...]. The question is, “Whose .ctor is it, anyway?”

 它的父级不是一个类型,而是一个结构化的类

9.最后一句话的翻译

And, of course, about the only possible way to represent a constructed type is by a signature.
That’s why TypeSpec records have only one entry, containing an offset in the #Blob stream, pointing
 at the signature. Personally, I think it’s a pity the TypeSpec record contains only one entry; a
Name entry could be of some use. We could go pretty far with named TypeSpecs.
 Most obvious possibilities include type aliasing and type forwarding.

 当然,表示这样一个结构化类型的唯一可能方式就是使用签名。这就是为什么TypeSpec记录只有一个字段,它包括了在#Blob流中的偏移量并指向这个签名。我个人认为,TypeSpec记录只包括一个字段是一个遗憾;Name字段多少会有些作用。使用命名TypeSpec会更好。极大可能是包括类型别名和类型转移(type forwarding

10.最后一句话的翻译

 The TypeSpec signature has no calling convention and consists of one SET, which, however,
can be fairly long. Consider, for example, a multidimensional array of function pointers
that have function pointers among their arguments.

TypeSpec签名没有调用约定,它是由一个SET组成,然而可以相当长。例如,考虑函数指针参数中有函数指针的多维数组的情况。

 

第9章 字段和数据常量
11.粗体字部分

The nonterminalsymbol <data_type> specifies the data type. (See Table 9-2.) The data
type is used by the IL assembler exclusively for identifying the size and byte layout of
<value>
(in order to emit the data correctly) and is not emitted as any part of metadata or the data
itself. Having no way to know what the type was intended to be when the data was emitted,
the IL disassembler always uses the most generic form, a byte array, for data representation.

非终结符号<data_type>指定了数据类型(见表9-2)。数据类型由IL汇编器专门用来识别<value>的大小和字节布局(为了流出正确的数据),而不是流出为元数据或数据本身的任意部分。


12.粗体字部分

Although instancefields cannot be mapped to data, it is possible to specify the positioning of
these fields directly. As you might remember from Chapter 7, a class or a value type can have
an explicit flag—a special flag indicating that the metadata contains an exact recipe for the
loader regarding the layout of this class
. This information is kept in the FieldLayout metadata
table, whose records contain these two entries:

虽然实例字段不能映射到数据,但可以直接指出这些字段的位置。在第7章介绍过,类或值类型可以有explicit标志——这个特殊的标志表示元数据包含了有关使用加载程序时这个类的布局信息的准确指令。这些信息保存在FieldLayout元数据表中,它的记录中包括了这样两个字段:


13.粗体字部分

Fields declared outside the scope of any class are known as global
fields. They don’t belong to a class but instead belong to the module in which they are declared. A module is represented
by a special TypeDef record with RID=1 under the name <Module>, so all the formalities that
govern how field records are identified by reference from their parent TypeDef records are observed.
 

任何在类的作用域之外声明的字段,都称为全局字段(global field)。全局字段不属于类,而是属于其声明所在的模块。因为模块是由名称<Module>中一笔RID=1的特殊的TypeDef记录表示的,所以可以观察到字段记录是如何被来自父一级的TypeDef记录的引用所标识的

 

第10章 方法
14.方法标志里面的保留标志,前面都没问题,关键是最后一句

•reqsecobj(0x8000). This methodcalls another method containing
security code, so it requires an additional stack slot for a security object.
This flag is formally under the Reserved mask, so it cannot be set explicitly.
Setting this flag requires emitting the pseudocustom attribute
System.Security.DynamicSecurityMethodAttribute. When the IL assembler
encounters the keyword reqsecobj, it does exactly that: emits the pseudocustom
attribute and thus sets this “reserved” flag. Since anybody can set this flag by emit-
ting the pseudocustom attribute, I wonder what the reason was for putting this
flag under the Reserved mask. This flag could just as well been left as assignable.

 

n         reqsecobj0x8000 因为这个方法调用了另一个包括安全代码的方法,所以它需要一个附加的栈槽给安全对象使用。从形式上讲,这个标志位于Reserved掩码之下,因此不能显示地设置它。设置该标志需要流出伪自定义特性System.Security.DynamicSecurityMethodAttribute。当IL汇编器遇到关键字reqsecobj时,它是这样严格执行的:流出伪定义特性进而设置这个保留标志。由于任何人都可以通过流出伪定义特性来设置这个标志,所以我很想知道为什么将这个标志放在保留标志下。这个标志还可以保留为可赋值的。


15.这个relax怎么翻译比较“雅”? 还有红色字体也不是很清楚

However, if the class has the beforefieldinit flag set (see Chapter 7), the invocation of
.cctor happens on “relaxed” (as it is called in the Ecma International/ISO standard) schedule—the .cctor
is supposed to be called any time, at CLR discretion, prior to the first access to a static field of the class.

然而,如果类设置了beforefieldinit标志(参见第7章),对.cctor调用的发生就会根据“relaxed(在国际Ecma/ISO标准中是这么称呼的)进度表——假定.cctor的调用时刻先于对类的静态字段的首次访问,由CLR判断。


 

第11章 泛型类型
16.以下两种翻译那种比较好?——GenericParamConstraint元数据表中的Constraint字段

•Constraint(coded token of type TypeDefOrRef). A token ofthe constraining type,
which can reside in the TypeDef, TypeRef, or TypeSpec table. The nature of the con-
straint (inheritance or implementation) is defined by the constraining type: if it is an
interface, then it’s an implementation constraint; otherwise it’s an inheritance con-
straint. (This reminds me of an old Navy adage: “Salute all that moves and paint all
that doesn’t.”) Since the CLR supports only single inheritance, no more than one
GenericParamConstraint record pertaining to a certain generic parameter can have
its Constraint entry referencing a noninterface type.
翻译1)由于CLR只支持单继承,只有一笔GenericParamConstraint记录——适合于一个特定的泛型参数——能够让它的Constraint字段引用一个非接口类型

翻译2)由于CLR2只支持单继承,所以只需一笔GenericParamConstraint记录,其附属于一个特定泛型参数,就能够有引用到非接口类型的Constraint字段。


第12章 泛型方法
17.virtually 怎么翻译?“虚”还是“实质上”?

The type parameters of the overriding method cannot be constrained more restrictively
than the type parameters of the overridden method. The reason for this requirement is simple.
Suppose you override the method void A<T>() with the method void B<U>() and constrain
Umore restrictively than T. Constraining a type parameter means narrowing the possible
choices of instantiation arguments, which means there will be types {Xi} that can substitute
for T but not for U. When you call virtually the instantiation of the overridden method, the CLR
checks the type argument compliance with the overridden method’s constraints, because it’s
the method that is called. So, you can call virtually void A<Xi>(), which means you will be
calling in fact void B<Xi>(), violating the constraints of U.

对覆写方法的类型参数的约束,不能比被覆写方法的类型参数更加严格。这种需求的原因很简单。假设使用方法void B<U>()覆写了方法void A<T>(),并且对U的约束比对T更加严格。对一个类型参数的约束表示缩小实例化参数的可择范围,这意味着存在类型{Xi}可以替代T但不能替代U当我们实质上调用被覆写方法的实例化时,CLR就会检查类型参数是否符合被覆写方法的约束,因为它是被调用的方法。所以,实质上我们可以调用void A<Xi>(),这意味着实际上我们将会调用void B<Xi>(),这就违反了U的约束。

 

第13章 IL指令
18.对引用的引用,我要怎么翻译才能说清楚

If the type is a reference type (and hence the this pointer is a managed pointer to
object reference), then the this pointer is dereferenced yielding the object reference,
and the virtual call is executed on this object reference.

如果类型是一个引用类型(从而this指针是指向对象引用的托管指针),那么this指针就会解除生成这个对象引用的引用[1],而虚调用会在这个对象引用上执行。


第18章 互操作
19.C数组,我不是很懂

C-style arrays can have a fixed length or a length specified by another parameter of the
method or a combination thereof, the total length being a sum of fixed (base) length and the
value of the length parameter.
Both values, the base length and the length parameter’s zero-
based ordinal, can be specified for the marshaler so that a vector of appropriate size can be
allocated. Chapter 8 describes the ILAsm syntax for specifying the array length. For example:

C风格的数组是定长的,或者,它的长度由方法的另一个参数或一个联合来指定,这个联合的全部大小是固定(基本)长度和长度参数值的总和。可以为封送器指定基本长度和长度参数这两个从0开始的序数值,从而可以为向量分配适当的大小。第8章讨论了指定数组长度的IL语法。例如: