.NET基础拾遗(1)类型语法基础和内存管理基础1
一、基础类型和语法
1.1 .NET中所有类型的基类是什么?
在.NET中所有的内建类型都继承自System.Object类型。
1.2 值类型和引用类型的区别?
在.NET中的类型分为值类型和引用类型,其共同点是都继承自System.Object,但最明显的区分标准却是是否继承自System.ValueType。常用的值类型包括:结构、枚举、整数型、浮点型、布尔型等等;而在C#中所有以class关键字定义的类型都是引用类型。
PS:严格来讲,System.Object作为所有内建类型的基类,本身并没有值类型和引用类型之分。但是System.Object的对象,具有引用类型的特点。这也是值类型在某些场合需要装箱和拆箱操作的原因。
(1)赋值时的区别
这是值类型与引用类型最显著的一个区别:值类型的变量直接将获得一个真实的数据副本,而对引用类型的赋值仅仅是把对象的引用赋给变量,这样就可能导致多个变量引用到一个对象实例上。
(2)内存分配的区别
引用类型的对象将会在堆上分配内存,而值类型的对象则会在堆栈上分配内存。堆栈空间相对有限,但是运行效率却比堆高很多。
(3)继承结构的区别
由于所有的值类型都有一个共同的基类System.ValueType,因此值类型具有了一些引用类型所不具有的共同性质,比较重要的一点就是值类型的比较方法:Equals。所有的值类型已经实现了内容的比较(而不再是引用地址的比较),而引用类型没有重写Equals方法还是采用引用比较。
1.3 装箱和拆箱的原理?
(1)装箱:CLR需要做额外的工作把堆栈上的值类型移动到堆上,这个操作就被称为装箱。
(2)拆箱:装箱操作的反操作,把堆中的对象复制到堆栈中,并且返回其值。
装箱和拆箱都意味着堆和堆栈空间的一系列操作,这些操作的性能代价是很大的,尤其对于堆上空间的操作,速度相对于堆栈的操作慢得多,并且可能引发垃圾回收,这些都将大规模地影响系统的性能。因此,我们应该避免任何没有必要的装箱和拆箱操作。
如何避免呢,首先分析装箱和拆箱经常发生的场合:
①值类型的格式化输出
②System.Object类型的容器
对于第①种情况,我们可以通过下面的改动示例来避免:
int i = ;
Console.WriteLine("The value is {0}", i.ToString());
对于第②种情况,则可以使用泛型技术来避免使用针对System.Object类型的容器,有效避免大规模地使用装箱和拆箱:
ArrayList arrList = new ArrayList();
arrList.Add();
arrList.Add("");
// 使用泛型数据结构代替ArrayList
List<int> intList = new List<int>();
intList.Add();
intList.Add();
1.4 struct和class的区别,struct适用于哪些场合?
首先,struct(结构)是值类型,而class(类)是引用类型,所有的结构对象都分配在堆栈上,而所有的类对象都分配在堆上。
其次,struct与class相比,不具备继承的特性,struct虽然可以重写定义在System.Object中的虚方法,但不能定义新的虚方法和抽象方法。
最后,struct不能有无参数的构造方法(class默认就有),也不能为成员变量定义初始值。
public struct A
{
public int a = ; // 这里不能编译通过
}
结构对象在构造时必须被初始化为0,构造一个全0的对象是指在内存中为对象分配一个合适的空间,并且把该控件置为0。
如何使用struct or class?当一个类型仅仅是原始数据的集合,而不需要复杂的操作时,就应该设计为struct,否则就应该设计为一个class。
1.5 C#中方法的参数传递有哪几种方式?
(1)ref关键字:引用传递参数,需要在传递前初始化;(ref 要求参数在传入前被初始化)
(2)out关键字:引用传递参数,需要在返回前初始化;(out 要求参数在方法返回前被初始化)
ref和out这两个关键字的功能极其类似,都用来说明该参数以引用方式进行传递。大家都知道,.NET的类型分为引用类型和值类型,当一个方法参数是引用类型时,传递的本质就是对象的引用。所以,这两个关键字的作用都发生在值类型上。
(3)params关键字:允许方法在定义时不确定参数的数量,这种形式非常类似数组参数,但形式更加简洁易懂。
But,params关键字的使用也有一定局限:当一个方法申明了一个params参数后,就不允许在其后面再有任何其他参数。
例如下面一段代码,定义了两个完全相等的方法:NotParams和UseParams,使用由params修饰参数的方法时,可以直接把所有变量集合传入而无须先申明一个数组对象。
class Program
{
static void Main(string[] args)
{
// params
string s = "I am a string";
int i = ;
double f = 2.3;
object[] par = new object[] { s, i, f };
// not use params
NotParams(par);
// use params
UseParams(s, i, f); Console.ReadKey();
} // Not use params
public static void NotParams(object[] par)
{
foreach (var obj in par)
{
Console.WriteLine(obj);
}
} // Use params
public static void UseParams(params object[] par)
{
foreach (var obj in par)
{
Console.WriteLine(obj);
}
}
}
1.6 浅复制和深复制的区别?
(1)浅复制:复制一个对象的时候,仅仅复制原始对象中所有的非静态类型成员和所有的引用类型成员的引用。(新对象和原对象将共享所有引用类型成员的实际对象)
(2)深复制:复制一个对象的时候,不仅复制所有非静态类型成员,还要复制所有引用类型成员的实际对象。
下图展示了浅复制和深复制的区别:
在.NET中,基类System.Object已经为所有类型都实现了浅复制,类型所要做的就是公开一个复制的接口,而通常的,这个接口会由ICloneable接口来实现。ICloneable只包含一个方法Clone,该方法既可以被实现为浅复制也可以被实现为深复制,具体如何取舍则根据具体类型的需求决定。此外,在Sys-tem.Object基类中,有一个保护的MemeberwiseClone()方法,它便用于进行浅度复制。所以,对于引用类型,要想实现浅度复制时,只需要调用这个方法就可以了:
public object Clone()
{
return MemberwiseClone();
}
下面的代码展示了一个使用ICloneable接口提供深复制的简单示例:
public class DeepCopy : ICloneable
{
public int i = ;
public A a = new A(); public object Clone()
{
// 实现深复制-方式1:依次赋值和实例化
DeepCopy newObj = new DeepCopy();
newObj.a = new A();
newObj.a.message = this.a.message;
newObj.i = this.i; return newObj;
} public new object MemberwiseClone()
{
// 实现浅复制
return base.MemberwiseClone();
} public override string ToString()
{
string result = string.Format("I的值为{0},A为{1}", this.i.ToString(), this.a.message);
return result;
}
} public class A
{
public string message = "我是原始A";
} public class Program
{
static void Main(string[] args)
{
DeepCopy dc = new DeepCopy();
dc.i = ;
dc.a = new A(); DeepCopy deepClone = dc.Clone() as DeepCopy;
DeepCopy shadowClone = dc.MemberwiseClone() as DeepCopy; // 深复制的目标对象将拥有自己的引用类型成员对象
deepClone.a.message = "我是深复制的A";
Console.WriteLine(dc);
Console.WriteLine(deepClone);
Console.WriteLine();
// 浅复制的目标对象将和原始对象共享引用类型成员对象
shadowClone.a.message = "我是浅复制的A";
Console.WriteLine(dc);
Console.WriteLine(shadowClone); Console.ReadKey();
}
}
其执行结果如下图所示,可以清楚地看到对深复制对象的属性的赋值不会影响原始对象,而浅复制则相反。
从上面的代码中可以看到,在深复制的实现中,如果每个对象都要这样去进行深度复制就太麻烦了,可以利用序列化/反序列化来对对象进行深度复制:先把对象序列化(Serialize)到内存中,然后再进行反序列化,通过这种方式来进行对象的深度复制:
[Serializable]
public class DeepCopy : ICloneable
{
...... public object Clone()
{
// 实现深复制-方式1:依次赋值和实例化
//DeepCopy newObj = new DeepCopy();
//newObj.a = new A();
//newObj.a.message = this.a.message;
//newObj.i = this.i; //return newObj;
// 实现深复制-方式2:序列化/反序列化
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this);
ms.Position = ;
return bf.Deserialize(ms);
} ......
}
[Serializable]
public class A
{
public string message = "我是原始A";
}
PS:一般可被继承的类型应该避免实现ICloneable接口,因为这样做将强制所有的子类型都需要实现ICloneable接口,否则将使类型的深复制不能覆盖子类的新成员。
PSP----值类型和引用类型的不同点.
.NET基础拾遗(1)类型语法基础和内存管理基础1的更多相关文章
- OC内存管理基础
OC 内存管理基础 一. retain和release基本使用 使用注意: 1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作) 2.你不想再使用(占用)某个对象,就 ...
- Objective-C内存管理基础
2011-05-11 15:45 朱祁林 http://zhuqil.cnblogs.com 字号:T | T 本文我们将介绍<Objective-C内存管理基础>,在iOS开发中,内存管 ...
- .NET基础拾遗(1)类型语法基础和内存管理基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
- .NET基础拾遗(1)类型语法基础和内存管理基础【转】
http://www.cnblogs.com/edisonchou/p/4787775.html Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串 ...
- .NET基础拾遗(1)类型语法基础和内存管理基础2
二.内存管理和垃圾回收 2.1 .NET中栈和堆 每一个.NET应用程序最终都会运行在一个OS进程中,假设这个OS的传统的32位系统,那么每个.NET应用程序都可以拥有一个4GB的虚拟内存..NET会 ...
- java基础(一):谈谈java内存管理与垃圾回收机制
看了很多java内存管理的文章或者博客,写的要么笼统,要么划分的不正确,且很多文章都千篇一律.例如部分地方将jvm笼统的分为堆.栈.程序计数器,这么分太过于笼统,无法清晰的阐述java的内存管理模型: ...
- JVM内存管理基础
JVM 虚拟机架构(图片来源: 浅析Java虚拟机结构与机制) JVM 内存区域 JVM会将Java进程所管理的内存划分为若干不同的数据区域. 这些区域有各自的用途.创建/销毁时间: (图片来源: ...
- Java基础学习总结(64)——Java内存管理
本文介绍的Java虚拟机(JVM)的自动内存管理机制主要是参照<深入理解Java虚拟机>(第2版)一书中的内容,主要分为两个部分:Java内存区域和内存溢出异常.垃圾回收和内存分配策略.因 ...
- Objective-C基础笔记(3)OC的内存管理
Objective-C的内存基本管理 在OC中每一个变量都保存着引用计数器,当这个对象的引用计数器为0的时候该对象会被回收.当使用alloc.new或者copy创建一个对象的时候,对象的引用计数器被置 ...
- Swift 值类型和引用类型的内存管理
1.内存分配 1.1 值类型的内存分配 在 Swift 中定长的值类型都是保存在栈上的,操作时不会涉及堆上的内存.变长的值类型(字符串.集合类型是可变长度的值类型)会分配堆内存. 这相当于一个 &qu ...
随机推荐
- C# 创建验证码图片
using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; us ...
- 连接Oracle11g数据库时遇到无监听,网络适配器无法建立等问题的一些解决办法
最近在用Java做一个学生成绩管理系统,打算用Oracle数据库.由于原先没接触过Oracle,所以安装完数据库后,连接数据库时遇到各种问题,网上搜索解决方案还是没有解决时,又重新安装了几次.终于在前 ...
- .net文件下载方法汇总
转载自:http://blog.sina.com.cn/s/blog_680942070101ahsq.html //TransmitFile实现下载 protected void Button1_C ...
- 简单描述一下XIB与Storyboards,简述它们的优缺点。
参考答案: 我倾向于纯代码开发,因此所提供的参考答案可能具有一定的个人感情,不过还是给大家说说自己的想法. 优点: XIB:在编译前就提供了可视化界面,可以直接拖控件,也可以直接给控件添加约束,更直观 ...
- 定义block块
一: 工程图 二: 代码区 AppDelegate.h #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <U ...
- VC防止程序被多次运行 互斥体方法
BOOL CXXXApp::InitInstance() //函数内添加代码 HANDLE hMutex=CreateMutex(NULL,TRUE,"test"); // 用于检 ...
- bootstrap小结
bootstrap总结 bootstrap总结 base css 我分为了几大类 1,列表 .unstyled(无样式列表),.dl-horizontal(dl列表水平排列) 2,代码 code(行级 ...
- 使用python发邮件
使用python发邮件 网上有很多发邮件的例子,本人在网上找了一份,稍加修改后使用 上源码 # encoding=utf-8 from email.mime.image import MIMEImag ...
- Android开发笔记之: 数据存储方式详解
无论是神马平台,神马开发环境,神马软件程序,数据都是核心.对于开发平台来讲,如果对数据的存储有良好的支持,那么对应用程序的开发将会有很大的促进作用.总体的来讲,数据存储方式有三种:一个是文件,一个是数 ...
- html5 百分比计算
这几天一直在看html5,看到了百分比的计算公式:目标元素的尺寸/上下文元素的尺寸=百分比尺寸.看到这个公式,有点懂,但是有不明白.对于目标元素很容易理解,但是对于上下文元素就不是很好理解了.试了一些 ...