网站开发语言有几种,网站建设系统 招标,红袖添香网站建设时间,龙华网站(建设信科网络)1.equal()和运算符的区别 由于C#中有值类型和引用类型#xff0c;那么相等也分为值相等和引用相等。先来看一个值类型简单的例子#xff0c;顺便也写了string类型的比较。 static void Main(string[] args){int n1 1;int n2 1;Console.WriteLine(n1n2);Console.WriteLine(n…1.equal()和运算符的区别 由于C#中有值类型和引用类型那么相等也分为值相等和引用相等。先来看一个值类型简单的例子顺便也写了string类型的比较。 static void Main(string[] args){int n1 1;int n2 1;Console.WriteLine(n1n2);Console.WriteLine(n1.Equals(n2));string str1 test;string str2 test;Console.WriteLine(str1str2);Console.WriteLine(str1.Equals(str2));Console.ReadKey();//结果是4个 True} 前2个结果为true我可以理解但是后2个为true我有点怀疑。一直记得不断有人说string是特殊的引用类型那这里应该是为str1和str2都分配了内存并str1和str2指向各自的内存但是结果却是true。我感觉很有可能是因为string类的“特殊”才发现自己一直都没有理解string类的特殊。百度之后发现原来是.NET做的优化由于str1和str2内容相同为了节省内存故让str1和str2指向同一个内存区域这样就不用为str2开辟空间了。这种技术是字符串驻留技术当CLR初始化时会创建一个内部的散列表键为字符串值Wie指向堆中字符串的引用JIT编译方法时添加str1和“test”到空的散列表里了str2赋值时会先看散列表里有没有相等的有的就直接指向相等的值的引用。编程中当把string作为参数传递时不会改变原有string对象的值因为每次赋值会另外开辟内存这就是string引用类型的特殊之处。 对于引用类型比较的是两个引用是不是指向同一个内存地址equal()方法比较的是两个对象指向的内存空间内容是否相同。下面是关于引用类型的代码。 class Program{static void Main(string[] args){//前面已提过这里str1和str2指向同一个引用故obj1和obj2表示的地址是相同的string str1 test;string str2 test;object obj1 str1;object obj2 str2;Console.WriteLine(obj1 obj2); //TrueConsole.WriteLine(obj1.Equals(obj2)); //True//这种情况的赋值内存没有做优化故obj3和obj4表示的是不一样的地址string str3 new string(new char[] { t, e, s, t });string str4 new string(new char[] { t, e, s, t });object obj3 str3;object obj4 str4;Console.WriteLine(obj3 obj4); //FalseConsole.WriteLine(obj3.Equals(obj4)); //True//当用new开辟新的空间创建对象时people1和people2肯定是指向了不同的内存地址//而且这是2个不同的对象我们不能只看它们有相同的方法相同的字段还要看它们的标识等环境变量//对于people3和peop4来说则是指向了同一块内存区域People people1 new People(小方);People people2 new People(小方);Console.WriteLine(people1 people2); //FalseConsole.WriteLine(people1.Equals(people2)); //FalsePeople people3 new People(小白);People people4 people3;Console.WriteLine(people3 people4); //TrueConsole.WriteLine(people3.Equals(people4)); //TrueConsole.ReadKey();}}class People{string name null;public string Name{get { return name; }set { name value; }}public People(string strName){name strName;}} 引用类型的变量是存在栈中但指向的是堆中的地址。操作符比较的是2个变量的值是否相等那对于引用类型也就是比较它们表示的地址是否相同。equal表示的是2个变量指向的堆中的内容是否相等。 2.思考为什么要封装字段 在上面的例子中已经形成习惯将字段封装成属性。我发现学习过程中大家总是这么写我有时候会想一下为什么要这样写但可能还没有做过实际的项目开发(没有因为不这样写被坑过),所有我一直没有想到最本质的原因。因为经常说为了保护数据的安全性不让直接读和写可是常常看到属性中是既有set又有get的那这样还不是直接读和写这样有什么区别呢今天既然又发现这个问题不能还不解决了。查阅了前辈的经验后我发现有2点说到本质了 1安全性也就是我可以在属性中进行判断比如age我可以在set的时候加一个if判断范围在0到150虽然也可以在外部判断但这样写是一定不会出错的 2当出现修改时我们可以在属性中进行修改而不必修改整个程序。举个例子比如业务需求改变为我们给一个变量赋值get时不是输出原来的值而是输出这个值乘以2如果该字段是public整个程序用到该字段的地方都要修改然而如果有属性那就很好办了只需在get时乘以2就可以了。 3.Dispose()、Close()、Finalize()的区别 Close()和Dispose()差不多只是因为Close这个词更加容易理解所以在Close()方法内部调用了Dispose()方法。Dispose方法在内部是去调用一个virtual的Dispose(bool)函数去释放资源。具有Dispose()方法的类是实现了IDisposable接口的很多类实现了IDisposable接口但是只提供Close()而不对外提供Dispose()。原因是这些类是显示实现接口这样的话实现类对象将无法调用Dispose()比如ClassA实现接口IDisposable接口如果要调用Dispose方法则只能调用((IDisposable)new ClassA()).Dispose()。这样做的目的也就是提供易于理解的Close()。例外有时候调用Close后我们还可以复活对象而Dispose一旦被调用就会实实在在的释放资源。 其实.NET最基本的释放资源方法是Finalize和Dispose这2个方法Finalize是用于释放非托管资源的Dispose可以释放所有资源即可释放托管资源又可以释放非托管资源。我们程序员是无法显示调用Finalize方法的这个方法当我们使用析构函数时才能被调用但是程序员并不会知道什么时候会调用析构函数这将由垃圾回收器控制。只有当垃圾回收器认为对象符合析构的时候才会调用程序退出时也会调用析构函数。为了提升性能我们最好不要使用空的析构函数因为如果类包括析构函数则Finalize队列中则会创建一个成员选项GC处理这个队列时如果选项内容为空那只会导致不必要的性能。 写到这里我心里有2个疑问上面提到建议我们不要使用空的析构函数可以理解是因为要提升性能可是我写析构函数不就是为了调用Finalize方法释放资源吗还有既然Dispose方法可以释放所有资源何必还要写一个Finalize方法呢直到敲了前辈们写的代码才有了答案。 public class People : IDisposable
{//前面我们说了析构函数实际上是重写了 System.Object 中的虚方法 Finalize, 默认情况下,一个类是没有析构函数的,也就是说,对象被垃圾回收时不会被调用Finalize方法 ~People(){// 必须以Dispose(false)方式调用,以false告诉Dispose(bool disposing)函数是垃圾回收器在调用Finalize时调用的 Dispose(false);}// 无法被客户直接调用 // 如果 disposing 是 true, 那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放 // 如果 disposing 是 false, 那么函数是从垃圾回收器在调用Finalize时调用的,此时会释放托管资源protected virtual void Dispose(bool disposing){// 那么这个方法是被客户直接调用的,那么托管的,和非托管的资源都可以释放 if (disposing){// 释放 托管资源 //......}//释放非托管资源 //......// 那么这个方法是被客户直接调用的,告诉垃圾回收器从Finalization队列中清除自己,从而阻止垃圾回收器调用Finalize方法. if (disposing)GC.SuppressFinalize(this);}//可以被客户直接调用 public void Dispose(){Dispose(true);}
} 从上面的例子中可以看出Finalize和Dispose释放了什么资源而且如果是Dispose方式释放资源后还会调用GC.SuppressFinalize(this)这个方法会告诉GC没必要再调用析构函数了因为已经使用Dispose方法释放资源了。这说明析构函数只是作为资源释放的一种补救措施同样的我们可以在析构函数中写释放非托管资源的代码最后再调用Finalize方法以保证资源被完全释放这样就万无一失了 声明本文原创发表于博客园作者为方小白如有错误欢迎指出 。本文未经作者许可不许转载否则视为侵权。转载于:https://www.cnblogs.com/fangyz/p/5293888.html