C#学习

来源:互联网 发布:梦神床垫 知乎 编辑:程序博客网 时间:2024/06/08 18:56

VS2015中运行C#程序时:  编译时按ctrl+F5就不会闪退


数组是同一数据类型的一组值
数组属于引用类型,因此存储在堆内存中
数组元素初始化或给数组元素赋值都可以在声明数组时或在程序的后面阶段中进行

  

  语法:

  数据类型[,…]  数组名称 = new 类型 [n,…];

 int[]  arrayHere= new int [6];

static void Main(string[]args){  intcount;  Console.WriteLine(“请输入准备登机的乘客人数");  count=int.Parse(Console.ReadLine());  //声明一个存放姓名的字符串数组,其长度等于乘客人数  string[]names = new string[count];  //用一个for 循环来接受姓名  for(inti=0;i<count;i++)  {  Console.WriteLine(“请输入第{0} 个乘客的姓名 ",i+1);  names[i]=Console.ReadLine();  }   Console.WriteLine(“已登机的乘客有:");  //用foreach 循环显示姓名  foreach(stringdispin names)  {  Console.WriteLine("{0}",disp);  }}

C#中的数据类型分为两个基本类别
值类型
  表示实际数据,只是将值存放在内存中,值类型都存储在堆栈中,intchar、结构
引用类型
  表示指向数据的指针或引用,包含内存堆中对象的地址,为 null,则表示未引用任何对象类、接口、数组、字符串


引用类型和值类型


System.Object方法

名称

说明

Equals

已重载。 确定两个Object实例是否相等。

Finalize

允许Object垃圾回收回收Object之前尝试释放资源并执行其他清理操作。

GetHashCode

用作特定类型的哈希函数。

GetType

获取当前实例的Type

MemberwiseClone

创建当前Object的浅表副本。

ReferenceEquals

确定指定的Object实例是否是相同的实例。

ToString

返回表示当前ObjectString


装箱和拆箱

1、装箱即将值类型转换为引用类型

int age = 17;

Object refAge= age;

2、拆箱即将引用类型转换为值类型。

int newAge= (int)refAge;

double newAge= (double)refAge;//错误,要具有相同类型

常用的还有as。用来将Object型还原成原本的引用或者值类型。

~ :将一个数按位取反,~操作符


switch...case

•表达式可以是int、字符或字符串
•C#不允许从一个case块继续执行到下一个case块。每个case块必须以一个跳转控制语句break、goto或return结束
•多个case标签可以对应一个代码块


循环结构的类型
while循环
do循环
for循环
foreach循环

do while for循环等价于自动的循环if判断

foreach循环用于遍历整个集合或数组

   语法:

   foreach(数据类型元素(变量)in集合或者数组)

   {

       //语句

   }

Foreach循环与for最大的差别就是不是单纯的if判断,而是节约效率的方式。因此推荐使用foreach。

Foreach不能对循环的对象进行修改


注意:Console.WriteLine("字母个数为:{ 0}",Letters);会出错。

           { 0}中不能有空格,应该为Console.WriteLine("字母个数为:{0}",Letters);


foreach(int i in array)

{

Console.WriteLine(t);//错误的写法Console.WriteLine(array[t]);

}


C#面向对象

结构程序设计的不足

1、数据与方法分离

2、代码不能重用

面向对象程序设计:封装、代码重用

*所有东西都是对象:变量和方法的集合。
*初级特性:OO最基本的概念,即类和对象。
*中级特性:OO最核心的概念,即封装、继承和多态。
*高级特性:由初级特性和中级特性引出的一些问题,如构造函数的使用、覆盖的规则、静态变量和函数等。


1、抽象数据类型是仅由数据类型和可能在这个数据类型上进行的操作定义的。

2、使用者只能通过操作方法来访问其属性,不用知道这个数据类型内部各种操作是如何实现的。 



*类:C#所有的代码都是在某一个类中,因此不可能在类之外的全局区域有变量和方法。
*对象:C#中的对象相当于一块内存区域,保存对象特有的类中所定义的数据。
*引用:C#中对于对象的操作全部通过引用进行。 

*声明一个类:访问修饰字class类名{变量声明,方法声明}
*声明一个对象引用:类名引用名Student student;
*创建一个对象:new类构造函数

student= new Student(); //如果缺少这一步编译器会报错

*使用对象:引用名.变量名/方法名(参数)

student.id= 200328013203194; //注意两个引用指向同一个对象和两个引用指向不同的引用的区别


类的成员分类
常量:表示与该类相关联的常量值。
字段:即该类的变量。
类型:用于表示一些类型,它们是该类的局部类型。
方法:用于实现可由该类执行的计算和操作。
属性:用于定义一些命名特性,通过它来读取和写入相关的特性。//public string MyVendor{ get { return vendor; } }
事件:用于定义可由该类生成的通知。
索引器:使该类的实例可按与数组相同的(语法)方式进行索引。
运算符:用于定义表达式运算符,通过它对该类的实例进行运算。
实例构造函数:用于规定在初始化该类的实例时需要做些什么。
析构函数:用于规定在永久地放弃该类的一个实例之前需要做些什么。
静态构造函数:用于规定在初始化该类自身时需要做些什么。 

类或结构的默认访问类型是internal
类中所有的成员,默认均为private

虽然默认了,但是项目中一般不允许使用默认,必须写明。

classFurniture{    const double salesTax = .065;    private double purchPrice;    private string vendor, inventoryID;    public Furniture(string vendor, stringinventID, double          purchPrice)    {        this.vendor = vendor;        this.inventoryID = inventID;        this.purchPrice = purchPrice;    }    public string MyVendor    { get { return vendor; } }    public double CalcSalesTax(doublesalePrice)    { return salePrice * salesTax; }}

class MyApp{    static void Main()    {        Furniture f = new Furniture("aaa", "001", 1.2);    Console.WriteLine(f.salesTax);        Console.WriteLine(Furniture.salesTax);        f.purchPrice = 10;//错误的写法,访问类型,类外的对象不可以访问私有的。类的成员函数中可以访问同类型实例对象的私有成员变量        string str = f.MyVendor;    }}

*C#中构造函数有三种:实例构造,私有构造和静态构造
构造函数

1、构造函数的特点:

    构造函数只能在对象创建时调用,即和new运算符一起被调用。

    –构造函数和类具有相同的名字。

    –构造函数可以有0个、1个或多个参数。

    –构造函数没有返回值。

    –每个类至少有一个构造函数,一个类可以有多个构造函数。

    –如果没有为类定义构造函数,系统会自动为其定义一个缺省的构造函数。缺省构造函数不带参数,作用是将实例变量都清零。

    –一旦为类定义了构造函数,则系统不会再为其定义缺省构造函数。

2、创建对象的格式:

   类名 对象名=new 构造函数(参数类表);

3、类的构造函数可通过初始值设定项来调用基类的构造函数,例如:

         public Student(string no, stringname,char sex,int age) :base(name,sex,age){ … }

     类的构造函数也可通过关键字this调用同一个类的另一个构造函数,例如:

         public Point() : this(0,20){ … }


析构函数

*C#支持析构函数。虽然C#能够自动进行垃圾回收,但对于某些资源,.Net不知道如何回收,所以需要人工的内存回收。

在.net 编程环境中,系统的资源分为托管资源和非托管资源。

–托管资源:如简单的int,string,float,DateTime 等等,是不需要人工干预回收的。

–非托管资源:例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。在使用完之后,必须显式的释放他们,否则会占用系统的内存和资源,而且可能会出现意想不到的错误。

*.net中超过80%的资源都是托管资源。尤其是文件,经常被用到

静态变量、方法
1、C#编程语言没有全局变量,static修饰的类变量针作用于整个类。
类变量可以被标记为public或private。如果被标记为public,不需要类的实例就可以访问
2、静态方法
*类方法是不需要类的任何实例就可以被调用的方法,在方法声明中用static关键字表示。
*类方法只能访问静态变量,访问非静态变量的尝试会引起编译错误。
*静态方法不能被覆盖成非静态的。
*main是静态的,因为它必须在任何实例化发生前被访问,以便应用程序的运行。

类成员
字段成员
    静态字段(属于类非对象,通常用来保存了当前类的个数)    
    只读字段(readonly修饰的,在声明时进行赋值,其他时候都不允许赋值)
                public class Class
{
  public readonly string str1=@”I am str1”;
}
//执行下面代码:
Class c1=new Class();
Console.WriteLine(c1.str1);
//c1.str1=“Change readonlyfiled”;错误
//如果在类定义中显式添加一个构造函数,可以在构造函数中改变只读字段的值。
Public class Class
{
  public readonly string str1=@”I am str1”;
  public Class(){ str1=“initialized by constructor!”;
}

方法成员
      声明格式:

   [访问修饰符]  返回类型  方法名称 ([参数列表])

   {  方法体;}   

参数列表包括指参数个数、参数数据、返回值类型       C#不支持多重实现继承,允许类型派生于多个接口。

方法重载:同一个作用域,方法名相同,参数列表不同

                 调用时根据参数的不同进行区分调用那个方法

方法重写:  不同作用域,方法名相同,  参数列表相同------覆盖

                   访问父类、子类皆调用子类的重写方法

1、C#中声明覆盖时,父类方法前加virtual关键字,表示该方法可以被覆盖;子类方法前加override,表示将方法覆盖。
2、当用于子类的行为与父类的行为不同时,覆盖机制允许子类可以修改从父类继承来的行为。 

       方法重定义:基类方法不做声明(默认为非虚方法),在派生类中使用new声明方法的隐藏----------隐藏,
                           访问父类调用父类的方法,访问子类则调用子类的方法
                  编译时多态---------重载, 运行时多态---------动态绑定(虚函数)
       静态方法:没有this引用;无需类实例;调用用类名.方法名;在静态方法体内不能够直接使用非静态成员
       子类重写了父类的虚方法sleep().
public class animal //基类
{
public virtual void sleep() //虚方法
{
Console.WriteLine("animal all need sleep");
}
}
public class fish:animal //派生类
{
public override void sleep() //重写虚方法
{
    Console.WriteLine("fish sleeping with eye_open");
}
}
public class dog:animal //派生类
{
public override void sleep() //重写虚方法
{
    Console.WriteLine("dog sleeping with eye_closed");
}
}

参数传递
1、值传递:值类型数据,形参改变实参不会改变;引用类型数据(eg: 对象stu),形参改变实参会改变
2、地址传递:形参改变实参会改变(引用传递使得形参和实参指向同一内存空间
        (1)ref:由调用方法初始化参数值。实参、形参中ref不能省 
        (2)out:被调用方法初始化参数值,可以不用初始化就作为参数传递给方法(避免多余的初始化)。实参、形参中out不能省 
3、C#中没有指针

     其实ref、out、看似好用,却会扩大变量的生命周期,使代码可读性下降。因此fxs的实际项目中会禁止使用


属性成员
1、属性主要用于描述和维护类对象的状态。从客户端看,对属性的访问就好像直接访问public字段成员,但在类内部是通过类方法访问的。
创建一个属性包括两步:

1).声明一个字段来存储属性值

2).编写一个属性声明,提供访问接口

2、属性的建立要使用属性声明,语法如下:

[访问修饰符]类型名  属性名

get

  {  return  字段;   }

   set

  { 私有字段=value; }

}

3、属性说明:

可以创建只读或只写属性,即只有get或set方法。
可以创建静态属性,用static关键字。
静态属性不与特定实例有关联,因此在静态属性的get和set方法内引用this是错误的。
静态属性使用类名访问,并且,与静态属性相配合的私有字段也应该是静态的。
编写自己类内部的代码时,调用自己类内部的方法,必须带上this.。这是标准化的要求

Fxs的项目中一般会禁止使用封装、继承(本质是代码复用)和多态

   封装把对象的所有组成部分组合在一起,三个作用

– 隐藏类的实现细节:使用方法将类的数据隐藏起来。

– 迫使用户去使用一个界面去访问数据:定义程序如何引用对  象的数据,控制用户对类的修改和访问数据的程度。

–  使代码更好维护:类的内部实现改变,对外接口可以不变

    继承提供了创建新类的一种方法,继承对开发者来说就是代码共享。

–  通过继承创建的子类是作为另一个类的扩充或修正所定义的一个类。

–  子类从超类(父类)中继承所有方法和变量。

–  子类和超类之间是特化与范化的关系。


class 子类类名:父类类名{子类体}

基类中,私有数据派生类不可以使用,保护数据和公有数据派生类可以使用
C#中,如果类声明时没有声明父类,那么缺省为Object类的子类。
C#中的所有类都是System.Object类的子类。
C#中,子类只能继承一个父类

派生类:
用base关键字显示调用基类构造函数
派生类会继承基类除了构造函数和析构函数的所有成员。
派生类调用构造函数时,会先调用基类的构造函数(默认调用没有参数的构造函数),析构调用顺序与构造顺序相反
如果需要调用基类中的同名方法,应该使用”base.方法名”来调用。

多态:
由于C#中System.Object类是所有类的祖先,所以可以用Object类型的引用指向所有类型的对象
继承机制引出多态机制,某一类型的引用变量可以指向该类或者其子类的对象
多态性是指不同的对象收到相同的消息时,会产生不同动作。
C#支持两种类型的多态性:

(1)编译时的多态性:通过重载类实现的,系统在编译时,根据传递的参数个数、类型信息决定实现何种操作。

(2)运行时的多态性:指在运行时,根据实际情况决定实现何种操作。C#中运行时的多态性通过虚函成员实现。 

动态绑定就是根据对象的类型决定调用哪个方法,而不是引用的类型。

类的方法使用virtual关键字修饰后就成为虚方法,包括两个步骤:

(1).对于基类中要实现多态性的方法,用virtual关键字修饰。不允许再有static,abstractoverride修饰符。

(2).对于派生类中的同名方法(覆盖)--相同的名称、返回类型和参数表,使用override关键字修饰。不能有newstaticvirtual修饰符

实现多态性的核心和实质:

使用基类的引用指向派生类的对象,当程序运行时,编译器会自动确定基类对象的实际运行时类型,并根据实际类型调用正确的方法。


密封类:

密封类不允许派生子类。C#提供一种不能被继承的类,称为密封类。密封类的声明方法是在类名前加上sealed修饰符。修饰符abstractsealed不能同时使用

密封方法:还可以在重写基类中的虚方法或虚属性上使用sealed修饰符。这将使您能够允许类从您的类继承,并防止它们重写特定的虚方法或虚属性。在下面的示例中,C从B继承,但C无法重写在A中声明并在B中密封的虚函数F

                    class A
{
    public virtual void F() { Console.WriteLine("A.F"); }
    public virtual void F2() { Console.WriteLine("A.F2"); }
}
class B : A
{
    public sealed override void F() { Console.WriteLine("B.F"); }
    public override void F2() { Console.WriteLine("A.F3"); }
}
class C : B
{
    // Attempting to override F causes compiler error CS0239.
    // protected override void F() { Console.WriteLine("C.F"); }


    // Overriding F2 is allowed.
    public override void F2() { Console.WriteLine("C.F2"); }


类嵌套:

内层类被看成是外层类的一个成员。

内层类的方法可以访问外层类的私有成员。

类外当需要访问内层类,可以使用“.”符号。

内层类使用private关键字修饰,就相当于外层类的私有成员,不能在外层类之外被访问到


抽象类与接口:

抽象方法:类中的方法不提供具体实现,但该类的派生类必须实现这些方法,这些方法在C#中称为抽象方法。

抽象类:通过关键字abstract进行标记将类声明为抽象类;包含抽象方法的类称为抽象类。

特点:
1、抽象方法必须是一个没有被实现的空方法,抽象方法是虚方法的特例。
2、抽象类中可以包含抽象方法和非抽象方法、变量等
3、一个非abstract类不能包含抽象方法。
4、抽象类是用来作为基类的,故不能直接被外部程序实例化,也不能被密封
5、不能创建抽象类的对象,但可以创建抽象类的引用。
6、构造函数和静态方法不能是抽象的。
7、子类若要覆盖抽象类的抽象方法时,要使用override关键字

当一个类从抽象类派生时,该派生类必须实际提供所有抽象成员的实现,否则,该派生类仍然是抽象类。如下面的示例所示:

abstract public Class A

    { public abstract void F(); //抽象方法}

abstractpublic Class B:A

     { public void G(){}; //附加方法}

public Class C:B

     {  publicoverridevoid F(){……}}  //抽象方法实现


接口:

语法结构:【访问修饰符】interface 接口标识符【:基接口列表】{ 接口体;}

接口成员访问权限为public,但不能加访问修饰符

接口成员不能有定义(即不出现 {})

口的成员必须是方法,属性,事件或索引器,不能包含常数、字段、运算符、实例构造函数、析构函数或类型。


实现:类要继承某个接口用 ":",在类的定义中给出接口中所定义方法的实际实现,类中接口实现必须显示声明为public

     publicinterface Interface1

   { voidfun1(inti);}//基成员

    publicinterface Interface2

     { newvoid fun1(inti);//隐藏基成员

     voidM1(inty);//添加新成员M1

      }

   publicclass cls1:Interface1


   {public void fun1(inti){…..}  }//实现接口方法

   publicclass cls2:Interface2

   {  public void fun1(inti){…..}

      public void M1(inti){…….}

   }

   对接口方法的调用与类中的情况相同。如果底层成员的命名与继承而来的高层成员一致,那么底层成员将覆盖同名的高层成员。
   接口支持多继承,故在多继承中,如果两个父接口含有同名的成员,这就产生了二义性,这时需要进行显式的声明。----------菱形
1、接口不是类,不能用new运算符,不能用接口创建对象
     x= new Comparable(); //错误
2、可以声明接口类型的引用,该引用只能指向实现了该接口的对象

      class Student : Comparable {...}

      Comparable x = new Student(); 

3、接口可以作为参数和作为返回值使用

privateIHomeworkCollector CreateHomeworkCollector(stringtype){

 switch (type)

 {

       case "student":

       collector =  new Student("Scofield",Genders.Male, 28, "越狱狱");

       break;

       ......

 } returncollector}

     接口作为参数:传递给它的值为实现该接口的对象
     接口作为返回值:返回一个实现了该接口的对象
4、is运算符
     功能:检查对象和类之间的关系。
     if(obj is classname)... 可用于确定接口是否可用。当obj是classname类或其子类的对象,则返回true;如果引用没有指向对象,编译出错。
5、as运算符
     完成的功能等价于:先是is检查,再执行对象类型转换。如果类型不兼容,as运算返回NULL。


接口和抽象类的对比:



C#中的结构体struct

     不支持继承(C++中可以),但可继承接口。它和类相似,可以包含构造函数,字段,属性,方法等,一般只是数据,有方法时用类

注意:

1. 结构不能包含显式的无参数构造函数。

2.  显式定义的构造函数必须带参数。

3. 对于结构中的实例字段成员,不能在声明时赋值初始化。

4. 在声明了结构类型后,可以使用new运算符创建结构对象。如果不使用new,那么在初始化所有字段前,字段保持未赋值状态且对象不可用。

5. 结构是值类型,类是引用型。

6. 结构不支持继承,但可继承接口。


以下任何一条,就应该使用类:

1.需要派生其他类型。

2.需要继承。

3.该类型作为方法参数传递。因为结构是值类   型,每次调用都要创建结构的副本。但结构放在数组中例外。

4. 该类型用作方法的返回类型。



错误类型:

1、语法错误:语法错误、缺少括号等在编译时确定易于确定

2、逻辑错误:错误的算法导致错误结果、公式错误等在执行过程中确定难以调试

3、运行时错误:内存泄漏、以零作除数、异常在运行时确定难以调试


错误:可预见,如信用卡号格式不对或口令不对。可由程序代码进行排除。
异常:与程序无关的外部原因造成。如数据表不可用或硬件故障等

system.EXception中的函数属性:

Message获取描述当前异常的消息

Source获取或设置导致错误的应用程序或对象的名称(程序集的名称)

TargetSite获取引发当前异常的方法

StackTrace获取当前异常发生时调用堆栈上的帧的字符串表示形式


引发异常的两种方式:
1、throw语句。使用显式throw 语句来引发异常。在此情况下,控制权将无条件转到处理异常的部分代码
2、使用语句或表达式在执行过程中激发了某个异常的条件,使得操作无法正常结束,从而引发异常

异常处理:

try...catch...finally

finally无论有否异常该代码都会执行;catch处理异常代码try中的。try...catch可以嵌套使用。

当CLR找不到处理异常的catch过滤器时,用定制处理未处理异常。

定制处理未处理异常

终止应用程序之前记录有关异常的信息。如果有足够的有关应用程序状态的信息,则可以采取其他措施,如保存程序数据以便于以后进行恢复。
建议谨慎行事,因为未处理异常时可能会损坏程序数据。
没有通用的方法适用于所有C#程序。
Windows窗体应用:

Applicatioin.ThreadException += new ThreadExceptionEventHandler(method);

Windows控制台应用:

Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(method);

定制类中实现System.Object方法:ToString()函数(默认情况下,返回类名。应覆盖此方法),Equals()函数(Equals方法必须和GetHashCode方法成对出现

 

序列化和反序列化:

序列化是将对象的状态存储到特定存储介质中的过程

反序列化则是从特定存储介质中的数据重新构建对象的过程 


工厂模式:
工厂类:担任这个角色的是工厂方法模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体的类实现。
抽象产品角色:担任这个角色的类是由工厂方法模式所创建的对象的父类,或她们共同拥有的接口。一般由接口或抽象类实现。
具体产品角色:工厂方法模式所创建的任何对象都是这个角色的实例,由具体类实现

System.Array

Array是抽象的基类,提供CreateInstance 方法来创建数组。

Array obj=Array.CreateInstance(typeof(string),10);

Array obj1=Array.CreateInstance(typeof(string),2,3,4);

SetValue()方法存储字符串;GetValue()方法检索数值组

点击打开链接  //ArrayList的使用

            int i = 100;            double d = 999.88d;            string s = "Hello World";            DateTime time = DateTime.Now;            ArrayList myList = new ArrayList();            myList.Add(i);            myList.Add(d);            myList.Add(s);            myList.Add(time);            myList.Insert(0, 200);            //myList.RemoveAt(0);            foreach (var p in myList)            {                Console.Write(p.ToString() + "  ");              }



在C#控制台或应用程序中只能有一个Main()方法。



0 0