第五章:基元类型、引用类型和值类型外文翻译资料

 2022-01-18 09:01

英语原文共 33 页,剩余内容已隐藏,支付完成后下载完整资料


第五章:基元类型、引用类型和值类型

在这一章中,我将讨论作为Microsoft.NET框架开发人员遇到的各种不同的开发类型。所有开发人员都必须熟悉这些类型的不同行为。当我第一次学习.NET框架时,我没有完全 了解基元类型、引用类型和值类型之间的区别。这些模糊的认识导致一些难以查找的bug以及性能问题。我希望通过在本章中解释一下这里的不同类型,能够帮助大家在提升代码效率的同时避免我曾遇到的一些麻烦。

基元类型

某些数据类型的使用非常普遍,许多编译器允许代码操作它们使用简化的语法。例如,可以使用以下方法分配一个整数:

System.Int32 a = new System.Int32();

相信大家都会感到用这样的语法来声明和初始化一个整数未免太过麻烦。幸运的是,许多编译器(包括c#)都允许我们这样来对整数进行声明和初始化:

int a =();

他的语法当然使代码更易读,当然,中间无论使用哪种语法,生成的语言(IL)都是相同的。任何数据类型直接支持的编译器称为基元类型。基本类型直接映射到类型存在于.NET框架类库(FCL)中。例如,在c#中,int映射直接到System.Int32类型。因此,以下四行代码全部正确编译并生成完全相同的IL:

int a = 0; // 最便捷的语法

System.Int32 a = 0; // 比较便捷的语法

int a = new int(); // I不便捷的语法

System.Int32 a = new System.Int32(); //最不便捷的语法

表5-1显示了在c#中具有相应原语的fcl类型。对于那些符合通用语言规范(CLS),其他语言将提供类似的原始类型。但是,语言不需要为非类兼容类型提供任何支持。

C语言规范指出,“作为一种编码风格,于使用关键字应该优于使用完整的系统类型名称。“我不同意这种论述;我更喜欢使用fcl类型名,并且完全避免使用基元类型名。事实上,我希望编译器甚至不提供基元类型名,并强制开发人员使用fcl类型名。以下是我的理由:

1.我发现开发人员都会感到困惑,不知道在代码中是使用string还是String。因为,在C#中string(关键字)实际上映射到system.string(fcl类型),所以两者之间没有区别,两者都可以使用。

2.在C#中,long映射到system.int64,但在另一种编程语言中,long可以映射到Int16或Int32。事实上,托管扩展C 将long视为int64。如果习惯使用一种语言,而转去阅读另一种语言编写的代码的人很容易误解。事实上,大多数语言甚至不会把long当作关键字,也不会编译使用它的代码。

3.fcl有许多类型的方法都将一些类型名作为方法名称的一部分。例如,BinaryReader类型提供了ReadBoolean、ReadInt32、ReadSingle等方法;而System.Convert类型也提供如ToBoolean、ToInt32、ToSingle等。虽然下面的代码是合法的,但其中含有float的代码在我看来总有些不太自然,这段代码的正确性也不太明显。

BinaryReader br = new BinaryRead(...);

float val = br.ReadSingle(); // 正确,但是不太自然

Single val = br.ReadSingle(); // 正确,而且感觉也很自然

出于所有这些原因,我将在本书中使用FCL类型名称。

在许多编程语言中,您需要编译以下代码,正确执行:

Int32 i = 5; //一个32位的值

Int64 l = i; // 隐式转换一个64位的值

但是,根据第4章中提出的对转型的讨论,大家可能会认为这段代码不能通过编译。 毕竟,System.Int32和System.Int64是不同的类型。 好,你会很高兴地知道C#编译器确实正确地编译了这段代码并且它运行为预期。为什么?

原因是C#编译器对基元类型有深入了解并应用它编译代码时拥有特殊规则。 换句话说,您的编译器选择识别常见的编程模式并产生必要的IL来使代码按预期工作。 具体而言,编译器通常支持与之相关的类型转换,文字常量和操作符相关的一些模式,如以下示例所示。

首先,编译器能够在原始类型之间执行隐式或显式转换:

Int32 i = 5; // 从Int32到Int32的隐式转换

Int64 l = i; // 从Int32到Int64的隐式转换

Single s = i; // 从Int32到Single的隐式转换

Byte b = (Byte) i;// 从Int32到Byte的显示转换

Int16 v = (Int16) s; //从Single到Int16的显示转换

如果转换是“安全的”,C#允许隐式转换,也就是说,不会丢失数据,例如将Int32转换为Int64。 但是如果转换是C#,则需要显式转换可能不安全。 对于数字类型,“不安全”意味着您可能会丢失精度或转换后的幅度。 例如,从Int32转换为Byte需要显式转换,因为大型Int32数字可能会丢失精度;从Single转换为Int64需要强制转换,因为Single可以表示数字比Int64更大的幅度可以。

请注意,不同的编译器可以生成不同的代码来处理这些强制转换操作。 例如,将一个值为6.8的Single转换为Int32时,有些编译器可以生成代码以在Int32中放置6,而其他人可以执行强制转换将结果舍入到7.顺便说一句,C#总是舍弃小数部分。 对于确切的规则C#遵循用于转换基元类型的内容,请参阅C#语言中的“转换”部分规格。

注意:如果您发现自己使用的类型是您选择的编译器没有支持作为原始类型,您或许可以使用静态方法System.Convert类型可以帮助您在不同的对象之间进行转换类型。 Convert类型知道如何在FCL中的各种核心类型之间进行转换:Boolean,Char,SByte,Byte,Int16,UInt16,Int32,UInt32,Int64,UInt64,Single,Double,Decimal,DateTime和String。Convert类型还提供静态ChangeType方法,允许将对象从一种类型转换为另一种任意类型,只要源对象的类型实现IConvertible接口,特别是ToType方法。

除了强制转换之外,基元类型还能以文本常量的形式出现,如下所示:

Console.WriteLine(123.ToString() 456.ToString()); // '123456'

此外,如果您有一个由文字组成的表达式,编译器可以在编译计算表达式,提高了应用程序的性能。

Boolean found = false; // 产生的代码将found设为0

Int32 x = 100 20 3; // 产生的代码将x设为123

String s = 'a ' 'bc'; //产生的代码将s设为“a bc”

最后,编译器自动知道在代码中使用时如何解释运算符(例如 , - ,*,/,%,&,

^,|,==,!=,gt;,lt;,gt; =,lt;=,lt;lt;,gt;gt;,〜,!, , - 等等):

Int32 x = 100;

Int32 y = x 23;

Boolean lessThanFifty = (y lt; 50);

Checked and Unchecked基元类型操作

程序员很清楚,对基元的许多算术运算都可能导致溢出:

Byte b = 100;

b = (Byte) (b 200); // b 的值将为44

重要:CLR只在32位和64位值上执行算术运算。因此,首先将b和200转换为32位值然后添加一起。 结果是必须转换为字节的32位值在结果可以存储回变量b之前。 C#没有隐式地为你执行这个转型,这就是为什么前面代码的第二行需要进入byte转型。

在大多数编程方案中,这种暗地里的溢出不是我们所希望的,而且如果没有被检测出来的花,他们往往会导致应用程序出现一些奇怪的行为。 在一些罕见的编程场景(例如计算哈希值或校验和),但是,这种溢出不是只有可以接受,但也是可取的。

不同的语言以不同的方式处理溢出。 C和C 不考虑溢出是一个错误,并且允许对溢出的值做wrap处理。如果发生了溢出,应用程序也会继续运行下去。 另一方面,Visual Basic始终将溢出视为错误,并在抛出检测到溢出时抛出异常。

CLR提供IL指令,允许编译器选择所需的行为。该CLR有一个名为add的指令,它将两个值相加。 添加指令不执行溢出检查。 CLR还有一个名为add.ovf的指令将两个值加在一起。 但是,add.ovf会抛出System.OverflowException的异常,当发生溢出时。 除了这两个用于添加操作的IL指令外,CLR也有类似的IL减法指令(sub / sub.ovf),乘法(mul / mul.ovf)和数据转换(conv / conv.ovf)。

C#允许程序员决定如何处理溢出。 默认情况下,溢出检查是关闭的。 这意味着编译器使用的版本生成IL代码不包括溢出检查的加,减,乘和转换指令。因此,代码运行速度很快 - 但开发人员必须确保不会发生溢出或者他们的代码旨在预测这些溢出。

让C#编译器控制溢出的一种方法是使用/ checked 命令行开关。 此开关告诉编译器使用溢出检查版本生成代码加法,减法,乘法和转换IL指令。 代码执行得更慢因为CLR正在检查这些操作以查看是否会发生溢出。 如果溢出确实发生,CLR抛出一个OverflowException异常。你应该设计应用程序的代码来处理此异常并优雅地恢复。

程序员不是全局打开或关闭溢出检查,而是更多可能想要逐个判断是否要进行溢出检查。 C#中checked和unchecked操作符为我们提供了这种灵活性。看下面的例子(假使

编译器默认情况下使用“不检查溢出”的方式来编译代码)

Byte b = 100;

b = checked((Byte) (b 200)); // 抛出OverflowException

在这个例子中,b和200首先被转换为32位值并加在一起;该结果是300.然后300转换为字节:这会生成OverflowException。 如果字节被转移到被检查的运算符之外,不会发生异常。

b = (Byte) checked(b 200); // b为44,不会抛出异常

除了checked和unchecked操作符外,C#还提供了checked和unchecked语句。 这些语句会导致检查块中的所有表达式都接受溢出检查或者不接受。

checked { //溢出检查开始

Byte b = 100;

b = (Byte) (b 200); //该表达式将被执行溢出检查

} // 溢出检查块结束

实际上,如果使用checked语句,现在可以使用 =运算符简化代码:

checked { // Start of checked block

Byte b = 100;

b = 200; // This expression is checked for overflow.

} // End of checked block

重要:因为checked操作符和语句只影响哪个加法,减法,乘法和数据转换IL的版本

生成指令,在已检查的运算符内调用方法或声明对该方法没有影响。 以下代码演示:

checked {

// 假设SomeMethod试图将400加载到一个byte中

SomeMethod(400);

// SomeMethod是否抛出异常要看其内的代码是否使用checked指令进行编译,这里与checked语句无关

}

这是使用checked和unchecked的一些原则:

1.在编写代码时,明确使用checked在您想要的块周围检查如果发生溢出,则抛出异常。 在第18章中,我将向您展示如何使用异常处理以从异常中正常恢复。

2.在编写代码时,明确地使用unchecked的块来查找您不想要的块即使发生溢出也会抛出异常。 这将是您想要的代码溢出无声地发生。

3.对于任何不使用checked或unchecked的代码,假设您这样做想要在开发时发生溢出时抛出,儿子发布后,便不希望在做溢出检查。

现在,在开发应用程序时,打开编译器的/ checked 开关进行调试建立。 您的应用程序将运行得更慢,因为系统将检查溢出任何未明确标记为checked或unchecked的代码。 如果异常发生时,您将轻松检测到它,并能够修复代码中的错误。 为了发布应用程序的构建,使用编译器的/ checked-开关,以便代码运行不会生成快速和异常。

重要:

System.Decimal类型是一种非常特殊的类型。虽然很多编程语言(包括C#和Visual Basic)考虑基元类型,CLR不。这意味着CLR没有直接操作Decimal的IL指令值。如果在.N

全文共37053字,剩余内容已隐藏,支付完成后下载完整资料


资料编号:[1064]

原文和译文剩余内容已隐藏,您需要先支付 30元 才能查看原文和译文全部内容!立即支付

以上是毕业论文外文翻译,课题毕业论文、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。