学习笔记:研读《C#入门经典》之易忽略的知识点

Posted ProMer_Wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记:研读《C#入门经典》之易忽略的知识点相关的知识,希望对你有一定的参考价值。

文章目录

学习笔记:研读《C#入门经典》之易忽略的知识点(一)

1.类型转换

1.隐式转换:从类型 A 到类型 B 的转换可以在所有情况下进行,执行转换的规则非常简单,

可以让编译器执行转换。

显式转换:从类型 A 到类型 B 的转换只能在某些情况下进行,转换的规则比较复杂,应进

行某种类型的处理。

1.1 隐式转换

隐式转换不需要做任何工作,也不需要另外编写代码。

1.2 显式转接

顾名思义,在明确要求编译器把数值从一种数据类型转换为另一种数据类型时,就是在执行显式转换

说白了就是,将某种类型,通过我们加 **(TypeB) 变量1 =(TypeB) 变量2 **;这种方式进行转换

注意:这只在某些情况下是可行的。彼此之间几乎没有什么关系的类型或根本没有关系的类型不能进行强制转换。

1.3 使用Convert命令进行显式转焕

2.值类型与引用类型

在 C#中,数据根据变量的类型以两种方式中的一种存储在一个变量中。变量的类型分为两种:

引用类型值类型,其区别如下:

值类型在内存的一个地方存储它们自己和它们的内容。

引用类型存储指向内存中其他某个位置(称为堆)的引用,而在另一个位置存储内容。

实际上,在使用 C#时,不必过多地考虑这个问题。到目前为止,所使用的 string 变量(这是引用

类型)与使用其他简单变量(大多数是值类型,例如 int)的方式完全相同。

值类型和引用类型的一个主要区别是,值类型总是包含一个值,而引用类型可以是 null,表示

它们不包含值。但是,可以使用可空类型(这是泛型的一种形式)创建一个值类型,使值类型在这个

方面的行为方式类似于引用类型(即可以为 null)。

3.深拷贝与浅拷贝

3.1 代码

namespace Ch09Ex03 

 

 class MyClass 

   public int val;   

 struct myStruct 

  public int val;  

 class Program 

  
 	static void Main(string[] args) 

  

 		MyClass objectA = new MyClass(); 
		MyClass objectB = objectA; 
 		objectA.val = 10; 
 		objectB.val = 20; 
 		myStruct structA = new myStruct(); 
         myStruct structB = structA; 
         structA.val = 30; 
         structB.val = 40; 
         Console.WriteLine("objectA.val = 0", objectA.val); 
         Console.WriteLine("objectB.val = 0", objectB.val); 
         Console.WriteLine("structA.val = 0", structA.val); 
         Console.WriteLine("structB.val = 0", structB.val); 
         Console.ReadKey(); 
  

  

 

(3) 运行应用程序,其结果如图所示。

3.2 示例的说明

这个应用程序包含两个类型定义。一个是结构 myStruct 的定义,它有一个公共 int 字段 val,另

一个是类 MyClass 的定义,它包含一个相同的字段(第 10 章介绍类的成员,如字段,现在只要知道

它们的语法是相同的即可)。接着对这两种类型的实例执行相同的操作:

  • 声明类型的变量。

  • 在这个变量中创建该类型的新实例。

  • 声明类型的第二个变量。

  • 把第一个变量赋给第二个变量。

  • 在第一个变量的实例中,给 val 字段赋一个值。

  • 在第二个变量的实例中,给 val 字段赋一个值。

  • 显示两个变量的 val 字段值。

尽管对两种类型的变量执行了相同的操作,但结果是不同的。在显示 val 字段的值时,两个 object类型有相同的值,而结构类型有不同的值。为什么会这样?对象是引用类型。在把对象赋给变量时,实际上是把带有一个指针的变量赋结了该指针所指向的对象。在实际代码中,指针是内存中的一个地址。在这种情况下,地址是内存中该对象所在的位置。在用下面的代码行把第一个对象引用赋给类型为 MyClass 的第二个变量时,实际上是复制了这个地址。

MyClass objectB = objectA;

这样两个变量就包含同一个对象的指针。

结构是值类型。其变量并不是包含结构的指针,而是包含结构本身。在用下面的代码把第一个结构赋给类型为myStruct的第二个变量时,实际上是把第一个结构的所有信息复制到另一个结构中。

myStruct structB = structA;

这个过程与本书前面介绍的简单变量类型如 int 是一样的。最终的结果是两个结构类型变量包含不同的结构。

3.3小结

从一个变量到另一个变量按值复制对象,而不是按引用复制对象(即以与结构相同的方式复制)

可能非常复杂。因为一个对象可能包含许多其他对象的引用,例如,字段成员等,这将涉及许多繁

琐的操作。把每个成员从一个对象复制到另一个对象中可能不会成功,因为其中一些成员可能是引

用类型。

.NET Framework 考虑了这个问题。简单地按照成员复制对象可以通过派生于 System.Object 的

MemberwiseClone()方法来完成,这是一个受保护的方法,但很容易在对象上定义一个调用该方法的

公共方法。这个方法提供的复制功能称为浅度复制(shallow copy),因为它没有考虑引用类型成员。

因此,新对象中的引用成员就会指向与源对象中相同成员的对象,在许多情况下这并不理想。如果

要创建成员的新实例(复制值,而不复制引用),此时需要使用深度复制(deep copy)。

可以实现一个 ICloneable 接口,以标准的方式来进行。如果使用这个接口,就必须实现它包含

的 Clone()方法。这个方法返回一个类型为 System.Object 的值. 我们可以采用各种处理方式,执行所

选的任何一个方法体得到这个对象。如果愿意,就可以进行深度复制(但执行过程不是必选的,所以

可以按照需要执行浅度复制)。

4.类型比较

4.1 类类型比较、值类型比较

对象之间的两类比较:

  • 类型比较

  • 值比较

在比较对象时,我们通常需要了解它们的类型,才能确定是否能够进行比较;

GetType()方法,和typeof()运算符一起使用,就可以确定对象的类型了

原理就是:所有的类都是从System.Object 中继承了这个GetType方法的;

if (myObj.GetType() == typeof(MyComplexClass)) 
  
 // myObj is an instance of the class MyComplexClass. 
 

那么其中起始还涉及到了一个类型判断的桥段:

从这其中我们就不得不提到is运算符了:

4.2 is运算符

概念:

is 运算符并不是说明对象是某种类型的一种方式,而是可以检查对象是否是给定类型,或者是否可以转换为给定类型,如果是,这个运算符就返回 true。

//通常我们在类鱼类之间传递数据的时候,可能就会涉及到类型之间的判断,并且通常应用于同一个函数对于不同的类型,我们需要做出不同的处理,也称为根据类型的筛选,亦或我们可以称为适配;当然我这里提到到并不是什么专业名词,仅仅只是通过生活中的实际例子做出的一种名称
eg.
    void Method(param object[] data)
    if(data is TypeA)
    
        doMethodA();
    
	else if(data is TypeB)
    
        doMethodB();
    

没有用到过这种方式的同学可能刚开始看有点怪,但是用过的同学自然就知道其中的妙处了;

5.装箱和拆箱

5.1 装箱和拆箱概念

封箱(boxing)是把值类型转换为 System.Object 类型,或者转换为由值类型实现的接口类型。拆箱(unboxing)是相反的转换过程。

//例如,下面的结构类型:
 struct MyStruct 
  
 	public int Val; 
  
//可以把这种类型的结构放在 object 类型的变量中,以封箱它:
 MyStruct valType1 = new MyStruct(); 
 valType1.Val = 5; 
 object refType = valType1; 
//其中创建了一个类型为 MyStruct 的新变量(valType1),并把一个值赋予这个结构的 Val 成员,然后把它封箱在 object 类型的变量(refType)中。

 	

//以这种方式封箱变量而创建的对象,包含值类型变量的一个副本的引用,而不包含源值类型变量的引用。要进行验证,可以修改源结构的内容,把对象中包含的结构拆箱到新变量中,检查其内容:

输出:valType1.Val = 6;

	valType1.Val = 6; 
 	MyStruct valType2 = (MyStruct)refType; 
 	Console.WriteLine("valType2.Val = 0", valType2.Val); 

执行这段代码将得到如下输出结果:

输出:valType2.Val = 5

但在把一个引用类型赋予对象时,将执行不同的操作。把 MyStruct 改为一个类(不考虑这个类
名不合适的情况),即可看到这种情形:
class MyStruct

public int Val;

//如果不修改上面的客户代码(再次忽略名称错误的变量),就会得到如下输出结果:

输出:valType2.Val = 6

5.2另一种方式的装箱与拆箱

也可以把值类型封箱到一个接口类型中,只要它们实现这个接口即可。例如,假定 MyStruct 类

型实现 IMyInterface 接口,如下所示:

interface IMyInterface 
  
    
  
 
 struct MyStruct : IMyInterface
  
 	public int Val; 
 

接着把结构封箱到一个 IMyInterface 类型中,如下所示:

MyStruct valType1 = new MyStruct();

IMyInterface refType = valType1;

然后使用一般的数据类型转换语法拆箱它:

MyStruct ValType2 = (MyStruct)refType;

从这些示例中可且看出,封箱是在没有用户干涉的情况下进行的(即不需要编写任何代码),但拆箱一个值需要进行显式转换,即需要进行数据类型转换(封箱是隐式的,所以不需要进行数据类型转换)。

读者可能想知道为什么要这么做。封箱非常有用,有两个非常重要的原因。首先,它允许在项的类型是 object 的集合(如 ArrayList)中使用值类型。其次,有一个内部机制允许在值类型上调用object,例如 int 和结构。这其实涉及到了上面所讲的值类型与引用类型的内容;

注意:要注意的是:在访问值类型内容前,必须进行拆箱;

6.可空类型

值类型(大多数基本类型,例如,int、double 和所有的结构)区别于引用类型(string 和所有的类)的一种方式:值类型必须包含一个值,它们可以在声明之后、赋值之前,在未赋值的状态下存在,但不能以任何方式使用。而引用类型可以是 null。

有时让值类型为空是很有用的(尤其是处理数据库时),泛型使用 System.Nullable 类型提供了使值类型为空的一种方式。

例如:

System.Nullable nullableInt;

这行代码声明了一个变量 nullableInt,它可以拥有 int 变量能包含的任意值,还可以拥有值 null。

所以可以编写如下的代码:

nullableInt = null;

如果 nullableInt 是一个 int 类型的变量,上面的代码是不能编译的。

前面的赋值等价于:

nullableInt = new System.Nullable();

与其他任何变量一样,无论是初始化为 null(使用上面的语法),还是通过给它赋值来初始化,都不能在初始化之前使用它。

来自深夜的程序猿的一个备注:Page 286,后续从这继续…zzzzzZZZ…

以上呢,便是我今日研读《C#入门经典》这本书籍的部分总结,希望能够对你有所帮助~ 也希望你能够点赞、评论吖~ 你们的点赞、评论就是我前进的动力!

作者:ProMer_Wang

链接:https://blog.csdn.net/qq_43801020/article/details/125342709

本文为ProMer_Wang的原创文章,著作权归作者所有,转载请注明原文出处,欢迎转载!

以上是关于学习笔记:研读《C#入门经典》之易忽略的知识点的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记:研读《C#入门经典》之易忽略的知识点

C#入门经典学习笔记 <chapter06 函数>

C#入门经典(v6) 读书笔记 (第一部分 C#语言)

《C#零基础入门之百识百例》(五十五)抽象类 -- 经典猫狗案例

《C#零基础入门之百识百例》(五十八)接口 -- 模拟银行存储

《C#零基础入门之百识百例》(八十一)泛型概念介绍 -- 泛型类/结构/接口/委托