c#基础知识

Author Avatar
Zhu Yuexin Dec 03, 2018

Hello World

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions

 //Compiler version 4.0, .NET Framework 4.5


 namespace Hello
 {
     public class HelloWorld
     {
         public static void Main(string[] args)
         {
             Console.WriteLine("Hello, World!");
         }
     }
 }

using表示使用命名空间,类似于Java中的导包,using System;之后就可以使用命名空间System中的所有类了。
在创建类的时候,需要指定类所属的命名空间,比如此处指定命名空间为Hello
Main函数即主函数,是程序的入口函数,可以看出和Java是很类似的。
示例程序中简单打印了一条Hello,World!语句,用到了Console类的WriteLine()方法,即在控制台打印一行语句,自动换行。这里的Console类属于命名空间System

在用Visual Studio新建类文件时,默认的命名空间是当前文件所在的目录,比如TestProject.Hello。事实上,命名空间和文件目录没有必然的联系,但为了不引起不必要的混淆,Visual Studio会将文件所在目录作为命名空间。

关键字

示例程序中如usingnamespacepublicclassstaticvoidstring等都属于c#的关键字,变量的命名不能和关键字冲突。c#中的关键字都是小写的,而且c#是大小写敏感的。

注释

  • 单行注释:用两条斜线//表示。
  • 多行注释:以/*开始,以*/结束,比如
    ```
    /*
  • 第一行注释
  • 第二行注释
    */
    ```
  • 文档注释:写在类、方法或属性的前面,用三条斜线///表示,比如
    /// 
    /// 这是一个测试类
    /// 
    class Test
    {…}
    

常量

c#中用关键字const定义常量,比如:

const double PI = 3.141592653

常用数据类型

  • 字符类型:char

  • 字符串类型:string

  • 整数类型:int

  • 双精度浮点型:double

c#的float类型需要在数值后面加上后缀fF,如float x = 3.5F;。否则会因为尝试将一个double类型数值存储到float类型中而发生编译错误。

类型转换

  • 自动类型转换:2中不同类型的数值运算,低精度类型会自动转换为高精度类型。比如8+2.4,int型数值会自动转换为double类型,再相加运算。
  • 强制类型转换:比如int i = (int)3.0;

标识符命名规则

程序中的变量名、常量名、类名、方法名,都叫做标识符。C#有一套标识符的命名规则,如果命名时不遵守规则,就会出错。这套规则简单说有下面三条:

  • 标识符只能由英文字母、数字和下划线组成,不能包含空格和其他字符。
  • 变量名不能用数字开头。
  • 不能用关键字当变量名。

运算符

赋值运算符

=+=-=*=/=%=

连续赋值,如x = y = 2;

算术运算符

基本的加+、减-、乘*、除/、求余%

+运算符还可以起到字符串连接作用,比如"my age is " + 18

自增++、自减--

比较运算符

=!=><>=<=

比较运算的结果,都是布尔类型bool,取值为truefalse。(打印时是TrueFalse)

逻辑运算符

逻辑运算符用来连接多个bool类型的表达式,实现多个条件的复合判断。包括:逻辑非!逻辑与&&逻辑或||

运算符优先级

运算符优先级顺序:

  1. 括号
  2. 一元运算符++--!
  3. */%
  4. +-
  5. ><>=<=
  6. ==!=
  7. &&
  8. ||
  9. 赋值运算符

条件控制结构

if-else结构

if...else结构:

if (a > 0)
{
    Console.WriteLine('Right');
}
else
{
    Console.WriteLine('Wrong');
}

if...else if...else结构:

if (a > 0)
{
    Console.WriteLine('Right');
}
else if (a == 0)
{
    Console.WriteLine('Wait');
}
else
{
    Console.WriteLine('Wrong');
}

如果条件分支中只包含一条命令,那么可以省略大括号,比如:

if (a > 0)
    Console.WriteLine('Right');
else if (a == 0)
    Console.WriteLine('Wait');
else
    Console.WriteLine('Wrong');

当出现多个 ifelse ,又没有{}来界定范围时,请参考下面2条原则:

  • 每一个 else 与前面离它最近的 if 配对
  • 多个 else 都与同一个 if 相近时,最内层的 else 优先配对

三元条件运算符

条件表达式 ? 分支1 : 分支2

当条件表达式为true时,执行分支1,为false时,执行分支2。

remark = age >= 18 ? "成年人" : "未成年人";

switch结构

int n1 = 3, n2 = 6;
char oper = '*';
switch(oper)
{
    case '+': Console.Write(n1 + n2); break;
    case '-': Console.Write(n1 - n2); break;
    case '*': Console.Write(n1 * n2); break;
    case '/': Console.Write(n1 / n2); break;
    default: Console.Write("运算符错误"); break;
}

switch中的变量只能是3中类型:int、char、string

循环结构

while循环

int y = 5;
while (y > 0)
{
    Console.Write(y+" ");
    y--;
}

do…while循环

int y = 5;
do
{
    Console.Write(y+" ");
    y--;
}
while (y > 0);

for循环

for (int y=5; y>0; y--){
    Console.Write(y+" ");
    y--;
}

continue和break

continue关键字可以跳过本次循环,直接进入下次循环

for (int x = 1; x < 10; x++)
{
    // 只打印奇数
    if (x % 2 == 0)
        continue;
    Console.Write(x);
}

break关键字可以直接结束整个循环

int x = 1;
while(true):
{
    Console.Write(x);
    x++;
    if (x > 5)
        break;
}

数组

声明:

数据类型[ ] 数组名 = new 数据类型[长度];

多种初始化方式:

char[] a = new char[3];
char[] b = new char[]{'A', 'B', 'C'};
char[] c = {'A', 'B', 'C'};
char[] d = new char[3]{'A', 'B', 'C'};  //三行语句等效

数组通过 Length属性返回数组长度:

string[] names = new string[3]{"Tom", "Bob", "John"};
for(int i=0; i

foreach循环

string[] t =new  string[]{"C","Sh","a","rp"};
foreach (string x in t)
{
    Console.Write(x);
}

二维数组

二维数组的声明:

int[,] arr = new int[2, 3];

二维数组的赋值和取值:

arr[1, 0] = 28;
Console.Write(arr[1, 0]);

控制台接收输入

string name = Console.ReadLine();
int age = int.Parse(Console.ReadLine());

断点调试

打断点:快捷键F9或者在要打断点的行左边双击。

F5调试程序,F10单步执行,F11进入执行的代码(属性内部,方法内部)。

面向对象

类定义

class Child
{
    public string _name;
    public string _sex;
    public int _age;
    public int _height;

    public void PlayBall()
    {
        Console.Write("Play football!");
    }
}

实例化对象

Child xiaoMing = new Child();
xiaoMing._name = "小明";
xiaoMing._sex = "男";
xiaoMing._age = 6;
xiaoMing._height = 120;
Console.WriteLine("我叫" + xiaoMing._name);
xiaoMing.PlayBall();

访问修饰符

  • public:公共
  • private:私有

封装

当有些字段不应该被直接访问时,就可以使用封装的手段隐藏该字段,但是会提供访问该字段的接口,也就是访问器。c#中使用属性用于字段的公开访问:

class Child
{
    private string _sex;  //隐藏字段
    public string Sex  //公开属性(访问器)
    {
        get{return _sex;}  //读
        set{_sex = value;}  //写(如果不提供写方法,那么这个属性就是只读属性)
    }
}

属性的类型应该和它对应的字段相同,属性的命名规范应该尽可能和它对应的字段名相近,但是首字母需要大写。

VS中封装的快捷键,选中某个字段,然后点击:CTRL + R + E

利用属性赋值和取值:

Child xiaoMing = new Child();
xiaoMing.Sex = "男";
Console.Write(xiaoMing.Sex);

通过set块约束属性赋值

class Child
{
    private int _age;

    public int Age
    {
        get{return _age;}
        set
        {
            //年龄限制为3-7岁
            if(value>=3 && value<=7)
                age = value;
        }
    }
}

方法重载

/// 
/// 吃糖
/// 
/// 糖的名称
public void EagSugar(string sugar)
{
    Console.WriteLine("Eat " + sugar);
}
/// 
/// 吃糖. 同一个类中,多个方法名字相同但参数(类型或数量)不同
/// 
/// 糖的数量
public void EatSugar(int count)
{
    if(count > 3)
    {
        Console.WriteLine("Eat too much!");
    }
}

构造方法

如果没有显示定义构造方法,则会有一个默认的无参数的构造方法

如果显示定义了构造方法,默认的无参构造方法就没有了。类中一定包含一个无参构造方法,所以如果定义了其他构造方法,需要显式的写一个无参构造方法。

可以存在多个参数不同的重载构造方法。

class Child
{
    private string _name;
    private string _sex;
    private int _age;
    private int _height;
       // 构造方法
    public Child()
    {

    }
    public Child(string name)
    {
        Name = name;
    }

    public string Name
    {
        get{return _name;}
        set{_name = value;}
    }
}

this关键字

class Child
{
    private string name;
    private string sex;
    private int age;
    private int height;
       // 构造方法
    public Child()
    {

    }
    public Child(string name, age)
    {
        Name = name;
        this.age = age;
    }

    public string Name
    {
        get{return name;}
        set{name = value;}
    }
}

对象初始化器

Child xiaoMing = new Child(){Name = "小明", Sex = "男"};  //其实调用的无参构造方法

值类型和引用类型

  • 值类型:int、char、double、bool
  • 引用类型:类、数组、接口

需要注意的是,string属于

值类型

值类型变量存储对象的值,赋值时会创建值的副本。修改任何一个副本,不会影响其他的副本。

比如:

int x = 5;
int y = x;
y--;

此时运算后的结果为:

x=5; y=4;

y的改变不会影响到x

引用类型

引用类型变量持有的是对象的引用,持有该对象引用的变量对该对象进行修改,也会影响到持有其引用的其他变量。

比如:

Child c1 = new Child();
c1.Name = "小明";
Child c2 = c1;
c2.Name = "小卡";
Console.WriteLine(c1.Name);
Console.WriteLine(c2.Name);

此时的输出结果为:

小明
小卡

c1c2两个变量都持有同一个对象引用,他们所做的操作直接影响到对象。

结构

值类型(int, char, double, bool …)都属于结构类型(struct),也具有 字段、属性和方法。

结构struct的定义:

struct Dog
{
    string _name;

    public string Name
    {
        get{return _name;}
        set{_name = value;}
    }

    public void Sing()
    {
        Console.WriteLine("Wang~");
    }
}

结构struct的实例化:

Dog dog = new Dog();
dog.Name = "小白";
dog.Sing();

结构也可以不用实例化:

Dog dog;
dog.Name = "小白";
dog.Sing();

结构struct总结:

  • 结构是值类型(类是引用类型)
  • 结构中可以定义字段、属性和方法

  • 结构中的字段不能赋初始值

  • 结构中不能显示定义无参构造方法
  • 结构类型的对象可以不实例化

枚举类型

enum Gender
{
    男, 女
}

使用:

Child c1 = new Child("小明", Gender.男, 6);
Child c2 = new Child("小华", (Gender)0, 5);  //用枚举值转换
  • 枚举是值类型
  • 枚举中不能定义字段属性和方法
  • 枚举值是从0递增的整数

ref关键字

值类型和引用类型在方法中的调用:

static void Main(string[] args)
{
    Child c1 = new Child("小明", 5);
    Growth(c1);  //按引用传参,方法修改形参,通常实参也会被修改
    Console.Write(c1.Age);
    Growth(c1.Age);  //按值传参,方法修改形参,实参不会被修改
    Console.Write(c1.Age);
}

static void Growth(Child child) //引用类型参数,按引用传参
{
    child.Age++;
}

static void Growth(int age) //值类型参数,按值传参
{
    age++;
}

上面的代码可以看出,在按值传参时,参数传递的是变量的值,方法中对形参的改变不会影响到实参。

ref关键字可以让值类型参数按引用传参:

static void Main(string[] args)
{
    int age = 3;
    Growth(ref age); //传递引用
    Console.Write(age);
}

static void Growth(ref int age) //值类型参数,按引用传参
{
    age++;
}

out关键字

out关键字也是按引用传递,可以用来处理多个方法返回值:

static void Main(string[] args)
{
    int age = 3;
    Growth(ref age);
    Console.Write(age);
}

static void Growth(int age)
{
    age++;
}