C# 第二天
来源:互联网 发布:长时间录像软件app 编辑:程序博客网 时间:2024/05/29 17:47
引用类型是在垃圾回收托管堆上分配的对象,默认情况下,当使用相等性测试的时候(==,!=)如果引用类型指向内存中的相同对象则返回true。
字符串是不可变的!所谓的改变只是返回了一个副本!
string s1 = "old string";s1 = "new string";
通过查看代码生成的CIL可以得出:多次调用了ldstr(加载字符串),oldstring的内存会被回收。
从中得出:一旦滥用string可能导致低效和代码膨胀!所以为了处理大量使用文本数据的情况,应该使用新的string结构。
StringBuilder
在System.Text这个相对较小的命名空间中,提供了StringBuilder类。StringBuilder可以直接修改字符串内部的内容,而不是获取一个修改后的副本,因此更高效。
默认情况下stringbuilder只能保存16个字符以下的字符串们,但是可以通过构造函数改变这个初始值(其实也可以自动扩展):
StringBuilder sb = new StringBuilder("this is a very lager string more than 16 character",256);
宽化:
隐式向上转换,例如short->int,char->int;并且不会丢失数据
窄化:
显示强制转换,如果不显示强制转换会报错!和宽化相对,但是这个要保证的确能转换,而且不能保证数据精度。
var:
隐式类型定义使用的var严格意义上来说并不是C#的关键字,所以var可以作为变量名或者参数名等,而且编译器也不会报错,但是当用于数据类型的时候,编译器会根据语境将其视为关键字。
var不仅能定义内置类型,而且还能定义自定义类型。
好处那么多,所以限制也是一大堆的:
只能用于方法或属性范围内的本地变量,用var定义返回值、参数或字段(就是类的变量)数据都是不合法的。
必须初始化!而且不能为null
别人阅读你的代码产生了困难
针对不能定义返回值,看下面的两个实例:
public var add(var x,var y){ return x+y;}//不能用于定义返回值和参数!public int add(int x,int y){ var z = x + y; return z;}//没问题
通过以上的限制可以知道,其实就是var能马上获知自己是什么类型,或许在内置类型的时候不明显,但是在我不关心要获得什么类型的时候作用非常大,比如说LINQ:
static void Ling QueryOverInts(){ int[] numbers = {10,20,30}; //LINQ查询 var subset = from i in numbers where i < 10 select i; foreach(var i in subset) { Console.Write(i); }}
上面的程序我们并不关心获得了什么类型,只是单纯的想要将获得的值赋给一个隐式的本地变量。
C#提供的switch可以判断string、枚举了,我们知道C/C++中是只能判断整形数值的。
静态方法可以直接被调用而不用创建类的实例
我们看到:
所有的数据类型都是继承自object,而一些是继承自ValueType,继承自ValueType的都会自动的在栈上分配(其他的则是在垃圾回收堆上分配),有一个可预期的生命周期,非常高效,可以调用object中预定义的所有类型都具有的方法(GetHashCode、Equals、ToString……)。
CS允许给方法的参数以下几种修饰:
1.空
按值传递,获得的是一个副本
2.out
输出传递,调用者必须也要使用out,必须给这个值赋值,调用者可以不用给这个值赋值。这是引用。
3.ref
引用传递,调用者必须也要使用ref,必须先初始化,这是引用。
2、3的例子:
public static void ChangeOne(string a,out string b) { a = "really"; b = "world!"; } public static void SwapString(ref string a, ref string b) { string temp = a; a = b; b = temp; } static void Main(string[] args) { string a = "hello"; string b = "world"; Console.WriteLine(a+b); ChangeOne(a,out b); Console.WriteLine(a+b); SwapString(ref a,ref b); Console.WriteLine(a+b); }
输出:
helloworldhelloworld!world!hello
4.params
不定参数,允许任意参数,但是参数的值必须在编译的时候就能确定而不是运行时。例如我想要获得一组数据的平均值,当然可以传递一个数组过去,但是用params后可以直接传递数值。
public static int Ave(params int[] data) { int sum = 0; for (int i = 0; i < data.Length; ++i) sum += data[i]; return sum / data.Length; } static void Main(string[] args) { int[] numbers = { 1,2,3,4,5}; Console.WriteLine(Ave(1,2,3,4,5)); Console.WriteLine(Ave(numbers)); }
多维数组:
1.矩形数组
每行都有相同列数
int[,] data = new int[2,5];
2.交错数组
int[][] data = new int[2][]; for(int i=0;i <2;++ i) { data[i] = new int[i+3]; }
一般二维至多维都是不推荐使用的,但是一维的效率非常高推荐使用。尽管定义的是int或者继承自ValueType的类型,但是这个数组仍然是分配在托管堆上的。
枚举类型可以是byte、short、int或者long其中的一种:
enum enumType : byte{ hello, world}
结构体的赋值运算符是拷贝了一个副本,因为struct是值类型,而class是引用类型,所以类
的赋值运算符是指向而不是赋值。
出于上述,现有下面的问题:
如果一个值类型中包含了一个引用类型调用赋值运算符会怎么样?
class A{ ...}struct B{ private A x1,x2; private int b; ...}static void main(string[] args){ B b1 = new B(...),b2; b2 = b1;}
上述就是一个struct(值类型)中包含看一个class(引用类型),当b2赋值给b1的时候回发生什么?
答:默认情况下,当值类型包含其他引用类型时,赋值将产生一个引用的副本。这样就有两个独立的结构,每一个都包含指向内存中同一个对象的引用(浅拷贝)。当想执行一个“深拷贝”,即将内部引用的状态完全复制到一个新对象中时,需要实现ICloneable接口。
按值传递引用类型:
这时候copy的是指向调用者对象的引用,所以和C++中的常量指针一样,可以改值,但是不能更改指针的方向:
static void ChangePerson(Person P){ P.Age = 100; P = new Person("hello",99);}
最终传递过来的P的年纪会改变成100,但是后面的重新赋值就不行。可能就是想引用吧,对这个别名的数据操作等价于对源数据的操作,但是不能重新起别名,也就是常量指针的意思。
按引用传递引用类型:
全部可变
综合上述:
值传递是绑定了调用者对象,除了不能改变对象,值可以变(限定于传递引用类型!值类型是一个拷贝)
引用传递完全可以改变
这里看一下值类型和引用类型的区别:
关于可空类型:
对于关系型数据库打交道来说,可能会遇到没有输入的情况,而不能单纯的表示为true/false。所以可以允许有null。但是可空类型只能应用于值类型,而不能是引用类型。
int? a = 10; int?[] b = new int?[10];Nullable<int> aa = 10;Nullable<int>[] bb = new int?[10];
代码中上面的?等价于下面的实现接口。
下面介绍一种简便运算符??:
??会判断为null马上赋值
int? a = null;int b = a ?? 100;
这样b就会输出100,否则是a;
编译器会保证类的字段先全部正确初始化,
对于C#和Java,其共同点都是先静态后普通。
区别在于:
C#是子类变量->父类变量->父类构造函数->子类构造函数(先变量后构造)
Java是父类变量->父类构造函数->子类变量->子类构造函数(先父后子)
和C/C++一样,一旦自己定义构造函数,必须显示自己写一个默认构造函数。
this:
1.this可以在参数相同时区分类和参数变量
看以下代码:
class Car{ private string name; private double cost; public void ChangeName(string name){ name = name; }}
编译器会警告:正在用变量本身设置该变量。
原因是参数变量的优先级大于类本身的,所以左边的name并不是调用者对象的name。
2.this可以实现串联构造函数
所谓串联构造函数是在有很多的构造函数时,将主要的构造工作交给参数最多的构造函数,避免冗[rǒng]余的代码。
原:
class Car { private string name; private double speed; private bool isSafe; public Car(){} //自己定义了构造函数必须要显示给一个默认构造函数 public Car(string name,double speed) { if (speed > 10) isSafe = false; isSafe = true; }//参数的speed而不是调用者的 public Car(double speed) { if (speed > 10) isSafe = false; isSafe = true; } ... }
串联构造:
class Car { private string name; private double speed; private bool isSafe; public Car(){} public Car(string name,double speed) { if (speed > 10) isSafe = false; isSafe = true; } ... }
这里还有一种解决方案:
class Car { private string name; private double speed; private bool isSafe; public Car(){} public Car(string name,double speed) { SetSafe(speed); } public Car(double speed) { SetSafe(speed); } private void SetSafe(double speed) { if (speed > 10) isSafe = false; isSafe = true; } }
可选参数和命名参数:
命名参数一般是在使用了可选参数的情况下使用
class Car { private string name; private string nickname; private double speed; private bool isSafe; public Car(){} public Car(string name,double speed = 10,string nick = "hello") { if (speed > 10) isSafe = false; else isSafe = true; this.name = name; nickname = nick; } }//在另外一个类中 public static void Main() { Car(); Car(100); Car(nick:"world"); }
- c#学习第二天
- C#学习第二天
- c#学习第二天
- C# 第二天
- C# 项目第二天
- c#学习第二天
- C#基础第二天
- C#基础语法(第二天)
- 21天学通C# 第二天
- C#学习之第二天
- C# 基础学习第二天
- C#高级编程第二天
- C#面向对象第二天总结
- C#学习第二天 基础语法规则
- 跟siki老师学C#第二天
- 0713第二天C#中的类
- C#开发第二天-第一个程序
- C# 实例练习(第二天)
- Python 类 setattr、getattr、hasattr 的使用
- android触摸事件分发机制
- 关于java里面注解的理解
- Python中的命令行解析工具介绍
- 如何快速入门产品经理?
- C# 第二天
- 转换xml格式的短信记录
- Android中TelephonyMnager的使用
- EventBus后台发送消息到前台
- C++ 关键字——friend
- phone attach PC:no permission
- mysql 5.7.9 免安装版 安装步骤及修改默认密码
- 启动 Eclipse 弹出“Failed to load the JNI shared library jvm.dll”错误的解决方法!
- 将自定义输入法设置为系统默认输入法