浪曦C#教程笔记.docx
《浪曦C#教程笔记.docx》由会员分享,可在线阅读,更多相关《浪曦C#教程笔记.docx(62页珍藏版)》请在冰豆网上搜索。
浪曦C#教程笔记
第一集
.NET组件包可以在微软官方网站下载和安装。
.NET中使用csc.exe这个编译器来编译C#程序。
要想运行这个程序需要从命令提示符进入到该文件所在的路径中(每一个版本的.NETFramework都有一个默认的路径来存储csc编译工具),命令执行之后就会提示当前.NET的版本,如果还要更方便的使用这个程序,就需要在系统变量中加入这个路径,这样无论以后在哪一个目录中都可以很方便的使用这个命令了,在设置环境变量的时候,有一个是Administrator变量,有一个是系统变量,前者只有在以Adminstrator身份登录的时候才会启动在其中设置的变量,而系统变量时无论你以何种身份登录,在其中设置的变量都会起作用,在系统变量中找到path,点击编辑,其中有很多路径,各个路径之间是以分号隔开的,如果我们要加入一个新的变量,可以在最后键入一个新的分号,然后再将.NET中csc.exe的路径加入其中就可以了。
这样在我们要执行一个命令的时候,系统会首先找到path路径,从其中查找你要的程序,从而去执行。
必须重新启动命令行窗口,刚才所做的设置才能够生效。
这样我们在任何的目录下都可以执行这个命令了。
命令提示符中使用鼠标的右键就可以粘贴复制的内容。
国际惯例,学习任何一种语言都是从“Helloworld!
”开始的。
后缀经为cs代表它是一个C#语言的源程序。
在工具文件夹选项中可以选择显示文件的全路径和全名称(包括后缀名)。
在C#中所有的事情都要在类中进行,所有要先声明一个类。
(在一般情况下,我们在编写C#程序之前都会现引入System这个命名空间,这里面包含了一般情况下我们需要使用的.NET基类。
)C#大小写敏感。
在运行中输入cmd执行就会出现命令行窗口,进入到我们建立的C#源程序文件所在的目录,键入csc文件名(包括扩展名),编译成功之后,再输入文件名(不包括扩展名),就会执行编译后的文件了。
Console类在System命名空间中。
分号代表一行代码的结束,这是语法约定。
.NETFramework由命名空间组成,每个命名空间都包含可在程序中使用的类型:
类、结构、枚举、委托和接口。
命名空间包含了一组类型或功能相近的类,当我们使用using引入这个命名空间之后就可以在程序中使用这些类。
大多数C#应用程序从一个using指令节开始,改接列出应用程序将会频繁使用的命名空间,避免程序员在每次使用其中包含的方法时都要制定完全限定的名称。
这意味着我们可以使用另外一种方法来使用命名空间,那就是把引入命名空间的语句删除掉,然后直接在要使用的类的前面加上它所在空间的名字并用.分隔。
也就是说我们可以在程序开头直接引用命名空间,也可以在使用的类前面加上命名空间前缀,但是要多次使用一个命名空间中的方法的时候,这样频繁书写前缀就得不偿失了,这时候就要在类的前面显式的用using引入这个命名空间。
通常一个软件项目包含的类成千上万,C#中使用命名空间来解决命名冲突的问题,在C#中命名空间可以用来定义类的范围,命名空间的引入需要使用using,而命名需要使用namespace关键字,后面紧跟需要命名的命名空间的名字,后面大括号中的所有代码都属于这个命名空间。
可以在一个命名空间下声明很多的类去实现不同的功能。
在一个源文件中可以声明多个命名空间,我们可以在一个命名空间中的类的方法里面调用另一个命名空间中的类中的方法,只需要创建相应的该类的对象就可以调用该类的方法,但是需要在被引用类的前面加上它所在命名空间的名字。
在C#中只要是使用了命名空间就不必担心自己所写的类和其他人所写的类有相同的名称。
使用命名空间的好处:
(1)代码可分布在多个文件中。
(2)命名空间具有扩展性。
(3)可以堆砌出层次式的类组织结构。
命名空间和类一样允许采用嵌套的结构,一个命名空间中可以包含其他的命名空间形成层次性结构,以更容易分组管理,这就像包的子包一样,如果想要引用方法,只需要将原来的包名前面加上外面嵌套的报名的名字和.就可以了。
在使用嵌套的命名空间的时候需要给出它的全路径名。
在同一个命名空间中的不同类可以分布在多个文件中存储,不过在使用嵌套类的时候要小心,它们必须在同一个文件中,不可以跨不同的文件进行存在,存储在两个文件中的方法要关联执行的时候需要同时进行编译,csc文件名一.cs文件名二.cs(中间用空格隔开),在命令行窗口中,F3是重复上次执行的命令。
编译好之后,运行的时候需要运行具有Main方法的主文件。
相同的命名空间可以分布在不同的文件当中(比如两个类都在一个命名空间之下,但是这个命名空间被分在了两个文件中),这两个类在在调用方法的时候,不需要再引入命名空间了。
这样使我们的管理方便了很多,在实际编程中如果程序较大,而且会频繁的更新,则需要把程序分布在不同的文件之中,就比如网络游戏,一个游戏的大小有1G,而且一个星期就需要更新一次,如果游戏的程序放在一个文件之中,通过网路更新的时候,再强健的网络也会受不了,但如果把一个程序分布在多个文件之中,哪个文件更改就下载哪个文件,这样就不会存在网络问题了。
在C#中可以把类库编译成DLL文件,供程序调用。
用csc/target:
library文件名(包括后缀名.cs)这时候就生成了DLL文件,library选项就代表着把cs文件编译成DLL文件,接下来把主文件和DLL文件进行关联并编译,使用命令为csc/reference:
PrintName.dllName.cs(reference表示跟某个DLL文件进行关联),Name.cs为主文件的文件名,两者一起编译,之后输入Name执行这个exe文件就能直接执行了。
如果对部分程序进行了修改,我们不需要再去编译exe文件,而只需要它所调用到的这个dll文件,这是对我们对程序进行修改时不至于重新编译整个程序,好处显而易见。
命名空间具有扩展性,你可以在命名空间中增加新类而不会影响到其它已经存在于此命名空间中的类,使得设计灵活性更高。
别名(Alias),使用using前缀指令可以有效的解决命名空间冲突问题,不过它还有一个特性,使用using加上简称,可以让定义在其它命名空间的类型更容易使用,大多数是应用在使用简短的名称来代替预使用的类。
使用别名使得程序代码更加简洁。
方法是,在源文件开头用usingA=命名空间名字;(这个命名空间名可能是由几个名字很长的命名空间组合而成),这时候在后面使用到这个较长的命名空间名字的时候就可以使用A来代替了,这就是别名。
这个程序原理其实很简单,它通过生成一个批处理文件并执行它,从而达到帮助我执行我想执行和的命令的效果。
在实际编程中很少碰到固定长度的数组,一般情况下数组的长度是未知的,有时候还需要动态的改变数组的长度,这就是动态数组,C#并不支持动态数组,需要使用动态数组时,可以使用ArrayList来代替,但是我们还是可以在数组声明时,动态生命其长度,用一个变量去声明数组的长度。
Inti=Int32.Parse(Console..ReadLine());
Main函数是静态函数,它只能直接调用域里面的静态成员。
在同一个类中,静态的Main函数调用本类中的非静态方法需要创建本类的对象,该方法是默认为私有的,可以在本类中直接调用,但是如果这一方法在别的类中,也同样需要创建这个非静态方法所在的类的对象,并将该非静态方法改为共有的,才能够调用。
ArrayList类位于System.Collections命名空间中,所以我们在使用的时候需要引入该命名空间。
ArrayList对象是比较复杂的数组,ArrayList类提供了Array类未提供的一些功能。
Array的容量是固定的,而ArrayList的容量可根据需要自动扩充,也就是会所Array不能在生存期改变它的容量,而ArrayList可以在生存期根据需要随意改变大小,ArrayList提供添加、插入或移除某一范围元素的方法,在Array中你只能一次获取或设置一个元素的值,Array可以具有多个维度,而ArrayList始终只是一维的。
如果使用true作为循环的条件,必定要在循环中某一处设置break来有条件的跳出。
Arr.Add(Str);可以为ArrayList方法添加一个元素。
Arr.Count表示ArrayList中的元素的个数。
Arraylist的访问方法和数组是一样的,都是使用下标。
声明不规则数组的时候,是按照int[][]arr=newint[3][];的格式的,仅仅声明一个维度,之后逐一赋值,每一次赋值都是将一个一维数组赋给其中的一个值,不规则数组又叫做锯齿数组。
一维数组是int[]格式,二维数组是int[,]的格式,三维数组是int[,,]的格式。
int[][][]arr是一个数组的数组的数组。
属性是类、结构和接口的命名成员,它们提供了通过访问器(accessor)读、写或计算私有字段值的灵活机制。
属性是这样的成员,它们提供灵活的机制来读取、编写或计算私有字段的值,可以像使用公共数据成员一样使用属性,但实际上它们是成为“访问器”的特殊方法,这使得数据在可被轻松访问的同时,仍能提供方法的安全性和灵活性。
给类的字段设为public意味着破坏了类的封装,这样会存在着潜在的危险,所以我们要将其设为private,这时候就需要分别声明方法,去读取和设置这两个字段,而这个方法我们可以将其设置为public,通过这个方法我们来读取字段的值。
由于属性存在简化了get和set,也就是读取和设置私有字段值的方法,我们可以把这两个方法生命在属性里面,这两个方法没有任何的修饰符。
属性的设置和读取很精妙。
属性实现了和方法一样的功能,它简化了前面为私有属性设置的两个方法的调用,属性的调用跟类的字段的调用的方法是一模一样的,赋值可以用=号,读取可以用.,表面上它是对一个类的字段进行操作,而实际上它还执行了一些额外的代码,去实现特定的功能。
在本例中,属性和私有字段的命名是一样的,只是首字母的大小写不同,这样的命名在C#中没有什么问题,但是工程中如果存在VB.NET的方法要访问这个类,就会出现问题,因为VB.NET是大小写不明感的,所以最好是在字段的前面加上诸如“m_属性名”这样的名字。
《属性二》
usingSystem;
classUser//储存用户的信息
{
privatestringm_Name;
privatestringm_Sex;
privateDateTimem_BirthDay;
privatestaticintm_LoginCount;
publicUser
{
m_LoginCount++;
}
publicstaticintLoginCount//静态的只读的属性
{
get
{
returnm_LoginCount;
}
}
publicDateTimeBirthday//只写属性
{
set
{
//年龄>107或者<3
If(valueDateTime.Now.Year-3){MessageBox.Show(“用户的年龄非法!
”);}//弹出对话框进行提示
}
else
{
m_Birthday=value;
}
}
publicintAge//只读属性
{
get
{
returnDateTime.Now.Year-m_Birthday.Year;
}
}
publicstringName//读写属性
{
get{returnm_Name};
set{m_Name=value};
}
publicstringSex//读写属性
{
get{returnm_Sex};
set{
if(value=’男’||value=’女’)
{
m_Sex=value};
}
else
{
Console.WriteLine(“性别只能为“男”或“女””);
}
}
}
classProperty
{
staticvoidMain()
{
//添加多个用户用于测试访问次数(静态属性)
Userzs=newUser();
Userls=newUser();
Userww=newUser();
Userml=newUser();
Console.WriteLine(User.LoginCount);//静态属性和静态字段是一样的,都是只能类名来访问,
//zs.Name=”张三”;
//zs.Sex=”男”;
//zs.Birthday=Convert.ToDateTime(“1980-1-1”);
Console.WriteLinr(“姓名:
”+zs.Name+”性别:
”+zs.Sex+”年龄:
”+zs.Age);
}
}
声明一个属性首先要声明一个和这个属性相关联的私有字段,接下来就是属性的声明,属性的声明实现了get和set两个访问器(accessor),在读取属性的时候会执行get访问器,给属性赋值时会执行set访问器,我们还可以在这两个访问其中假如可以实现特定功能的代码,属性的读取和写入与字段的读取与写入没有什么区别,只是再写入属性值的时候它会自动的去执行set访问器里面的代码,而在读取属性值的时候,它会自动的去执行get访问器里面的代码。
属性的种类
属性有四个种类:
可读写(Read/Write)属性:
需要实现get和set访问器。
只读(ReadOnly)属性:
需要实现get访问器。
只写属性(WriteOnly):
需要实现set访问器。
Static属性:
只能访问静态数据。
对话框是窗体控件,所以在使用对话框之前要导入SystemWindows.Form命名空间。
(MessageBox.Show();)
If(valueDateTime.Now.Year-3)//年龄>107或者<3
{MessageBox.Show(“用户的年龄非法!
”);}
另外,年龄属性应该根据时间动态的计算,年龄属性应该声明为只读属性,
下面实现一个静态属性,可以在类里面声明一个计数器,记录网站的被访问次数,在最开始应该声明一个私有的静态字段,然后声明一个静态属性和它关联,我们希望User类的实例被创建的时候,它的私有静态字段就会自动加1,要实现这个功能,需要使用构造函数,
静态属性需要实现static修饰符,并且它只能访问类里面的静态字段而不能访问实例字段。
SharpDevelop软件实现了.NETFramework很多功能,它是开源免费的。
方法是包含一些列语句的代码块,它实现了一定的功能,并拥有一个便于识别的名称,也便于在程序中调用,方法的名称不可重复,大小写视为不同,这个不可重复是指在同一个类或者在同一个命名空间下,如果出于不同的命名空间之下,方法的名称是可以不同的。
returnDateTime.Now;就是返回当前的时间。
Now是属性而不是方法。
C#是一门完全面向对象的语言,它摒弃了全局变量,但是如果两个以上的方法要共享一个变量怎么办?
如果要共享一个变量就要将类声明在类(Class)阶层,和局部变量不同的地方在于,类级别变量的生命周期是在此类加载到内存时就会自动的分配内存空间,要等到此对象被CommonLanguageRuntime(公共语言运行时)的垃圾回收器(GarbageCollector)回收,才会释放掉所用的内存。
C#的垃圾回收不在程序中进行,而是由.NET在同一适当的时候进行,这大大的减少了写出不合理代码的可能性,也增加了编程的效率,使得我们可以更加专注于程序的逻辑。
静态变量就意味着当类存在的时候,它就已经存在并初始化了,加载类的时候就已经会给它分配内存,不管这个类(这个静态变量所在的类)创建了多少个实例,所有的实例始终共享该静态变量,这样通过static这个关键字,我们声明静态变量,就可以实现全局变量的功能了,这时候所有的实例操作的都是一个变量,因为他们是共享的,但是如果去掉这个static,那么它们就会各自在自己的对象中保存一个变量进行操作,各自的变量是互不影响的。
方法的参数传递机制,有三种:
1值参数(ValueParameter)方法名称(参数类型参数名称[,参数类型参数名称])
2.引用参数(ReferenceParameter)方法名称(ref参数类型参数名称[,ref参数类型参数名称])
3.输出参数(OutputParameter)方法名称(out参数类型参数名称[,out参数类型参数名称])
在参数前面加上ref关键字就是引用传递.调用的时候和声明的时候都应该加上ref关键字.
下面声明一个带输出参数的方法:
publicstaticvoidOutPutMethod(outinti)
{
i=0;
i++;
}
控制离开当前方法之前必须对out参数i赋值,即out参数规定参数i在方法体内必须被初始化,正确的方法是不要在方法外对i进行初始化,应该只在外面声明,而在方法内进行初始化。
调用该方法:
inti;OutPutMethod(i);Console.WriteLine(i);
使用引用参数和使用out参数所得出的结果是一样的.
用图例来讲解三种参数传递机制的区别:
值参数的调用:
在Main()函数中声明一个I,这时候就会在内存的堆栈中就会专门为i开辟出一段新的内存空间并存放它,而在调用进行值调用的时候,就会在堆栈中开辟一块新的内存空间,并把i的值拷贝过来,赋给方法中的i值,接着进行的操作是对方法中的i,也即对复制而成的i进行一系列的操作.而Main函数中的i并没有做任何的改变.
引用参数的调用
在Main()函数中,声明了一个变量j,使得堆栈中开辟了一块内存空间来存储j,这时候调用方法的时候将j作为参数传递进去,而由于ref关键字的存在使得这次传递的是一个内存中的指针,也就是这次它会告诉所调用的方法:
我传入的这个参数地址在哪里,你直接去调用就可以了,所以该方法通过这个指针找到参数i在内存中的地址而去操作它.也就是说Main函数中的变量和调用的方法所用的变量指向的是同一块内存地址,他们的变量是一样的.
输出参数的调用
输出参数的调用和引用参数的调用非常类似,他所传送的也是一块内存的地址,而被调用的方法outputMethod(out
Inti)根据这个参数找到这个内存地址,并对里面的i进行操作,唯一不同的是,在输出函数中必须对参数进行初始化。
而相对于引用传参而言,必须在调用函数之前对变量进行初始化,而不是在函数(方法)内部对参数进行初始化,否则是通不过编译的。
从CLR(CommonLanguageRuntime公共语言运行时)角度来看,关键字out和关键字ref是等效的,这就是说,无论使用哪个关键字,都会生成相同的元数据和IL代码(IL(IntermediateLanguage)是.NET框架中中间语言的缩写,使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直接执行的机器代码,而是一种中间语言IL(IntermedateLanguage)的代码。
)。
我们知道.NET中使用的是中间代码,无论你是用C#还是使用VB.NET来编写代码,最后都会生成相同的IL代码。
但是C#编译器将两个关键字区别对待,在C#中,这两个字的区别在于哪个方法负责初始化引用对象。
如果方法的参数标记为out,那么调用者不希望在调用方法之前初始化对象,被调用的方法不能读取对象的值,而且被调用的方法必须在返回之前为对象赋值。
如果方法的参数标记为ref,那么调用者必须在调用方法之前首先初始化参数的值,被调用的方法可以读取参数或为参数赋值。
想方法传递可变数量的参数:
为了将方法声明为可以接受可变数量参数的方法,使用params关键字.
classMetohd
{
staticintaddi(paramsint[]values)
(
intsum=0;
foreach(intsuminvalues)
sum+=i;
returnsum;
)
staticvoidMian()
{
Console.WriteLine(Addi(1,2,3));
}
}
在params关键字后面跟的必须是一个数组,在这里是一个整型的数组,这样它就可以接收多个参数了,并把多个参数存放在values这个数组里面,这个函数的功能是把所有参数值相加并返回,传递参数1或者1,2,3会返回1和6通过params关键字我们可以向方法传递可变数量参数的参数。
下面我们尝试数组作为方法的参数:
classMetohd
{
staticintPrintArr(int[]arr)
(
for(inti=0;iarr[i]=i;
)
staticvoidMian()
{
int[]arr={100,200,300};
PrintArr(arr);
for(intiinarr)
Console.WriteLine(i+“”);
}
}
会打印0,1,2,数组是一个引用类型的变量。
值类型和引用类型的差别
类型分为值类型和引用类型,类型区分为着两大类的主要原因在于执行性能与内存资源管理的不同。
由于值类型变量直接在堆栈中存储该类型的值,此类型在内存的使用上以及访问效能上比引用类型更好,因为引用类型存放的是指向实际对象的指针,因此访问对象时必须多进行一次访问对象的操作,方可获取数据。
而引用类型的变量必须分配多余的内存来存放虚函数指针及线程同步块,对于内存的需求较大,而是用引用类型的优点是回收站会自动替您管理分配在托管堆(ManagedHeap)当中的内存。
值类型变量和引用类型变量的优缺点:
值类型
引用类型
变量中存放
真正的数据
指向数据的引用指针
内存空间分配
值类型直接分配在堆栈(Stack)
分配在托管堆(ManagedHeap)
内存需求
一般来说较少
较大
执行效能(速度)
较快
较慢
内存释放点
执行超过定义变量的作用域
由回收站负责回收
是否可以为NULL
不可以
可以
在内存释放的时间点上,值类型只要执行超过定义变量的作用域,它就会自动被释放。
而引用类型会由回收站负责回收,也就是说不需要我们专门去写代码,去回收。
值类型有一缺点,在特定的情况下值类型需要进行装箱和拆箱操作,这个会使得其执行效能变慢。
inti=0;当我们声明一个变量i的时候,就会在堆栈中声明一个新的空间,并把0存放在里面,也就是说值类型变量的值是直接存放在堆栈中的,int[]i={1,2,3};之后,1,2,3,这三个元素就会被存放在托管堆中,而堆栈中存放的则是指向这个托管堆的地址。
当我们把数组作为参数传递给方法之后,它所传递的其实是堆栈中的这个对应1,2,3的1,2,3在托管堆当中的地址,而方法接收到这个地址之后,方法就会根据这个地址到托管堆当中直接操作这个数组里面的元素了。
这就是我们把数组作为值参数传递给方法的时候它的值仍会改变的原因。
当然,这里也有以外,比如说字符串,字符串也是一个引用类型的变量。
classMetohd
{
staticintSetStr(strings)
{
s=”987654”;
}
staticvoidMian()
{
string