我们发现Parse只能转换一个类型,如果输入的文本不止一个类型,如“250+”,这是转换成int型会报错
这时我们可以用TryParse方法,他需要两个参数,第一个为输入的文本,第二个为接收结果的变量
int result;
bool re = int.TryParse("250=+2",out result);
返回值为bool类型,告诉程序员能不能转换成功,如果成功,就把值赋予result,失败则将0赋予result
所以TryParse方法返回两个值
因为方法声明在栈上,所以方法内部声明的变量,无论是值类型还是引用类型都在栈上,值类型存的是数据,引用类型存的是引用
当方法执行完,被销毁时,对应在堆里的数据就变成了垃圾
通过垃圾回收器回收这块空间
**GC(垃圾回收器)**是CLR中一种针对托管堆自动回收释放内存的服务
GC线程从栈中的引用开始跟踪,从而判定哪些内存是正在使用的,若GC无法跟踪到某一块堆内存,那么就认为这块内存不再使用了,即为可回收的
注意点:
值类型比较比的是值,引用类型比较比的是引用
要清楚各种类型在赋值时,赋值的位置
值类型传的是值,引用类型传的是引用
形参object类型,实参传递值类型,则装箱
因为拆装箱比较消耗性能,方法可以用重载、泛型避免,
int a = 1;
object o = a;
string str = "" + a;//装了
string str2 = a.ToString();//没装
因为object是引用类型,所以,他的值应该在堆上,开始a 的值在栈上,把a的值赋给o,这个过程就涉及了拆装箱
这些操作是“比较”耗性能的(跟栈内复制比起来)
装箱box:值类型隐式转换为object类型或由此值类型实现的任何接口类型的过程
在上面代码的基础上:
int b = (int)o;
拆箱操作也是“比较”消耗性能的(比装箱好一点)
拆箱unbox:从object类型到值类型或从接口类型到实现该接口的值类型的显式转换
内部机制:
string s1 = "abc";
string s2 = "abc";
string s3 = new string (new char[]{'a','b','c'});
string s4 = new string (new char[]{'a','b','c'});
bool re = object.ReferenceEquals();//结果是true
bool r1 = object.ReferenceEquals();//结果是false
可知s1和s2是同一个字符串,而s3和s4不是同一个字符串
字符串有一个字符串池的概念,字符串赋值时会先检测字符串池中有没有已经存在该字符串,若有会直接返回他的引用,如果不存在,则开辟空间
s3,s4都new了新空间,所以不是同一个引用
用于:提高内存利用率
在上面代码的基础上:
s1 = "bbc";
字符串的不可变性
重新开辟空间,存储新字符串,再替换栈中引用
问:为什么不能直接替换原来的字符串呢?
答:如果,替换的内容过长,会占用到后面的空间,如果后面的空间已经被使用,则会导致内存崩坏
string str = "";
for(int i = 0;i < 10;i++)
{
str = str + i.ToString();
}
Console.WriteLine(str);
每次拼接产生新的对象,替换引用(原有数据变成一个垃圾)
非常消耗性能,尽量不要使用
可以写成:
StringBuilder builder = new StringBuilder
for(int i = 0;i < 10;i ++){
builder.Append(i);
}
string result = builder.ToString();
StringBuilder有一个默认值,虽然添加一个就会扩大一次空间,但是达到默认值也会报错
可变字符串,在创建时,一次开辟可以容纳多个字符大小的空间,尽量填入准确的空间,如果扩大时,还会产生垃圾
优点:避免产生垃圾,可以在原有空间修改内容
适用性:频繁对字符串操作(增删改)
builder.Insert()插入
builder.Replace()替换
builder.Remove()移除
在字符串中使用则是:
s1 = s1.Insert(0,"s1");
旧的字符串也成为了垃圾,这里一样是新开辟了一块空间