文章目录
- 一. 一个简单的helloworld
- 二. 程序结构
- 三. 类型和变量
- 四. 表达式
- 1. f(x)
- 2. []
- 3. typeof
- 4. default
- 5. new
- 6. checked和unchecked
- 7. sizeof
- 8. 移位
- 9. is和as
- 10. null合并
- 五. 语句
- 六. 类和对象
- 1. 可访问性
- 2. 类型参数
- 3. 基类和派生类
- 4. 字段
- 5. 方法
- 6. 参数
- 7. 扩展方法(this参数)
- 8. 虚方法、重写方法、抽象方法和方法重载
- 9. 属性
- 10. 索引器
- 11. 默认值
- 六. 结构
- 七. 数组
- 八. vs使用技巧
一. 一个简单的helloworld
using System;class helloworld
{//静态方法可以在不引用特定对象的情况下运行,所以main是程序入口点static void Main(){Console.WriteLine("helloworld");}
}
写完的程序保存在hello.cs中,可以在vs的命令行执行csc hello.cs
编译该程序,会生成一个hello.exe可执行程序集,然后.\hello.exe
就会输出helloworld。
二. 程序结构
C#中的关键组织结构包括:程序、命名空间、类型、成员和程序集。
- 程序声明类型,类型包含成员,并被整理到命名空间中
- 类型:包括类和接口
- 成员:包括字段、方法、属性和事件
- 编译完的C#程序会被打包到程序集中,扩展名为.exe或.dll
using System;
namespace Acme.Collections
{public class Stack{Entry top;public void push(object data){top = new Entry(top,data);}public Object pop(){if (top == null) throw new InvalidOperationException();object result = top.data;top = top.next;return result;}class Entry{public Entry next;public object data;public Entry(Entry next, object data){this.next = next;this.data = data;}}}
}
使用csc /t:library acme.cs
编译成库(不含main入口点的代码),并生成acme.dll
程序集。
程序集是包含代码和元数据的自描述功能单元,所以不用在C#中引入#include指令和头文件,只需要在编译时引用特定的程序集。
下面程序使用了acme.dll中的Acme.Collections.Stack类
using System;
using Acme.Collections;class Test
{static void Main(){Stack s = new Stack();s.push(1);s.push(2);s.push(3);Console.WriteLine(s.pop());Console.WriteLine(s.pop());Console.WriteLine(s.pop());}
}
test.cs和acme.dll可以用编译器的/r
选项来引用程序集:csc /r:acme.dll test.cs
,就可以生成test.exe可执行程序集,执行就可以输出3 2 1。
三. 类型和变量
C#的五个类别是用户可定义的类别:类类型、结构类型、接口类型、枚举类型和委托类型
- 类类型:包括数据成员(字段)和函数成员(方法、属性),支持单一继承和多形性
- 结构类型:包括数据成员和函数成员,值类型不需要堆分配,不支持用户指定的继承,均隐式继承object
- 接口类型:定义公共函数成员,实现接口的类或结构必须提供接口函数成员的实现
- 委托类型:对具有特定参数列表和返回类型的方法的使用。通过委托,可以将方法视为可分配给变量并可作为参数传递的实体,类似于函数指针,但比指针安全
- 枚举类型:枚举类型的值集与基础类型的值集相同。
- 类、结构、接口和委托都支持泛型,所以可以用其他类型参数化。
默认构造函数
所有值类型都隐式声明为默认构造函数的公共无参数实例构造函数,默认构造函数返回一个名为零初始化实例,该实例成为值类型的默认值:
- 简单类型
- 有无符号整型:默认值为0
- char:`\x0000`
- float:0.0f
- double:0.0d
- decimal:0.0m
- bool:false
- 枚举类型,默认值为0,转换为类型E
- 结构类型:默认值是通过将所有值类型的字段设置为其默认值,并将所有引用类型字段设置为null生成的值
- 可以为null的类型:默认值为HasValue属性为false且value属性未定义的实例。
值类型的默认构造函数使用new运算符进行调用,不会产生构造函数的调用
允许结构类型声明参数化实例构造函数,
每种C#类型都直接或间接地派生自object类类型,object是所有类型的最终基类。只需将值视为object,即可将引用类型的值视为对象,通过执行装箱或取消装箱操作,值类型的值被视为对象。值类型的值通过执行装箱和取消装箱的操作被视为对象。
using System;class Test
{static void Main(){int i = 123;//int -> objectobject o = i;//object -> intint j = (int)i;}
}
在C#中,将零整数或浮点值转为布尔值,通过将整数或浮点值与零相比较来实现,或通过将对象引用显式比较为null来实现。
四. 表达式
- 可重载的一元运算符:+,-,!,~,++,–,true,false
- 可重载的二元运算符:+,-,*,/,%,&,|,^,<<,>>,==,!=,>,<,>=,<=
- 无法重载成员访问、方法调用、=、&&、||、??、?、=>、checked、unchecked、new、typeof、default、as和is
1. f(x)
namespace ConsoleApp1
{class Program{static void Main(string[] args){Caculator c = new Caculator();//委托:不用去直接做这个事情,通过委托间接的调用一个方法Action action = new Action(c.PrintHello);action();}}class Caculator{public void PrintHello(){Console.WriteLine("hello");}}
}
2. []
namespace ConsoleApp1
{class Program{static void Main(string[] args){// 泛型:泛型类本身不是一个完整的类,需要和其他类型组合在一起称为一个完整的类。Dictionary<string, Student> stuDic = new Dictionary<string, Student>(); for(int i = 0; i < 100; i++){Student student = new Student();student.Name = "S_" + i.ToString();student.Score = 100 + i;stuDic.Add(student.Name, student);}Student student6 = stuDic["S_6"];Console.WriteLine(student6.Score);}}class Student{public string Name;public int Score;}
}
3. typeof
typeof帮助我们查看一个类型的内部结构,类型的内部结构术语叫Metadata,里面包含这个类型的基本信息,比如:什么名字、属于的名词空间、父类、包含的方法和属性。等成员拿到这些信息后就可以根据这些信息决定自己的程序逻辑。
namespace ConsoleApp1
{class Program{static void Main(string[] args){Type t = typeof(int);Console.WriteLine(t.FullName);Console.WriteLine(t.Name);Console.WriteLine(t.Namespace);int c = t.GetMethods().Length;foreach(var mi in t.GetMethods()){Console.WriteLine(mi.Name);}Console.WriteLine(c) ;}}
}
4. default
帮助获取一个类型的默认值
namespace ConsoleApp1
{class Program{static void Main(string[] args){// 结构体类型int x = default(int);Console.WriteLine(x);// 引用类型Form form = default(Form);Console.WriteLine(form==null);//枚举类型Level level = default(Level);Console.WriteLine(level);}}enum Level{Low,Mid,High}
}
5. new
new可以调用示例的初始化器
Form form = new Form() { Text = "hello", FormBorderStyle = FormBorderStyle.SizableToolWindow};
form.ShowDialog();//new和var组合:为匿名类型创建对象,并用隐式类型变量引用这个实例
var person = new{Name = "je", Age = 34};
Console.WriteLine(person.GetType().Name); // <>f__AnonymousType0`2
new还是关键字
namespace ConsoleApp1
{class Program{static void Main(string[] args){Student stu = new Student();stu.Report();CsStudent cs = new CsStudent();cs.Report();}}class Student{public void Report(){Console.WriteLine("I'm a student.");}}class CsStudent: Student{//子类把父类的方法隐藏了new public void Report(){Console.WriteLine("I'm a Csstudent.");}}
}
6. checked和unchecked
C#默认采用unchecked
checked:编辑器要检查程序有没有溢出
namespace ConsoleApp1
{class Program{static void Main(string[] args){uint x = uint.MaxValue;Console.WriteLine(x);string binStr = Convert.ToString(x, 2);Console.WriteLine(binStr);//法一try{uint y = checked(x + 1);Console.WriteLine(y);}catch (OverflowException ex){Console.WriteLine("there is overflow!");}//法二checked{try{uint y = x + 1;Console.WriteLine(y);}catch (OverflowException ex){Console.WriteLine("there is overflow!");}}}}
}
7. sizeof
sizeof只能用来获取结构体数据类型的实例在内存中的字节数,string和object不是结构体
在默认情况下,可以获取自定义数据类型的字节数,但是需要放在不安全的上下文中
8. 移位
右移:如果是整数补0,如果是负数补1
9. is和as
x is T:如果x是T,则返回true,否则返回false
x as T:类型转换,返回类型为T的x,如果x的类型不是T,则返回null
10. null合并
class Program
{static void Main(string[] args){int? x = null;//如果x为null,则y的值为1,否则为xint y = x ?? 1;Console.WriteLine(y);}
}
五. 语句
foreach语句
static void Main(string[] args){foreach(string s in args){Console.WriteLine(s);}}
yield语句
foreach (int i in ProduceEvenNumbers(9))
{Console.Write(i);Console.Write(" ");
}
// Output: 0 2 4 6 8
IEnumerable<int> ProduceEvenNumbers(int upto)
{for (int i = 0; i <= upto; i += 2){//在迭代中,提供下一个值yield return i;}
}
Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {2, 3, 4, 5, -1, 3, 4})));
// Output: 2 3 4 5Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {9, 8, 7})));
// Output: 9 8 7IEnumerable<int> TakeWhilePositive(IEnumerable<int> numbers)
{foreach (int n in numbers){if (n > 0){yield return n;}else{//显示迭代结束yield break;}}
}
六. 类和对象
1. 可访问性
2. 类型参数
class Test
{static void Main(){//使用泛型类时,必须为每个类型参数提供类型自变量Pair<int,string> pair = new Pair<int, string> { First = 1, Second = "two" };int i = pair.First;string s = pair.Second;}//pair的类型参数是TFirst和TSecondpublic class Pair<TFirst, TSecond>{public TFirst First;public TSecond Second;}
}
3. 基类和派生类
//Point的基类是objectpublic class Point{public int x, y;public Point(int x, int y){this.x = x;this.y = y;}}//Point3D的基类是Pointpublic class Point3D : Point{public int z;public Point3D(int x, int y,int z):base(x, y){this.z = z;}}//类类型的变量可以引用相应类的实例或任意派生类的实例Point a = new Point(10,10);Point b = new Point3D(10,10,20);
类继承基类的所有成员(实例和静态构造函数除外)和析构函数。
类类型的变量可以引用相应类的实例或任意派生类的实例。
4. 字段
- 字段是一种表示与对象或类型(类与结构体)关联的变量,也叫成员变量。与对象关联的字段称为实例字段;与类型关联的字段称为静态字段,由static修饰
- 无显示初始化时,字段获得其类型的默认值,所以字段永远不会未被初始化
- 实例字段初始化的时机:对象创建时;静态字段初始化的时机:类型被加载时
- 只读字段
readonly
5. 方法
静态方法(用static修饰)通过类访问,只能直接访问静态成员
实例方法通过类实例访问,能够访问静态和实例成员
6. 参数
- 值参数:修改值参数不会影响为其传递的自变量
-
值类型
-
引用类型,并且创建新对象
-
引用类型,只操作对象,不创建新对象
-
- 引用参数:用
ref
修饰,引用参数指的存储位置与自变量相同,必须对自变量进行明确赋值,用于需要修改实际参数值的场景 - 输出参数:用
out
修饰,与引用参数类似,调用方提供的自变量的初始值不重要,用于出返回值外还需要输出的场景 - 数组参数:用
params
修饰,参数数组只能是方法的最后一个参数
class Test
{static void Main(string[] args){int res = CaculateSum(1,2,3);Console.WriteLine(res);}static int CaculateSum(params int[] arr){int sum = 0;foreach (int i in arr){sum += i;}return sum;}
}
- 具名参数:提高代码的可读性,参数位置不再受参数列表设计的约束
static void Main(string[] args)
{PrintInfo(name: "tom", age: 12);
}
static void PrintInfo(string name, int age)
{Console.WriteLine("Hello {0},you are {1}", name, age);
}
7. 扩展方法(this参数)
不修改目标数据类型源码的情况下为目标数据类型“追加”方法
- 方法必须是共有的、静态的,即被
public static
修饰 - 必须是形参列表中的第一个,由this修饰
- 必须由一个静态类(一般类名为SomeTypeExtension)来统一收纳SomeType类型的扩展方法
class Test
{static void Main(string[] args){double x = 3.1415926;Console.WriteLine(x.Round(2));}
}
static class RoundExtension
{public static double Round(this double x, int digits){double res = Math.Round(x, digits);return res;}
}
Linq扩展方法
using System;
using System.Collections.Generic;
using System.Linq;class Test
{static void Main(string[] args){List<int> list = new List<int>() { 3, 12, 14, 15 };bool res = list.All(i => i > 10);Console.WriteLine(res);}
}
8. 虚方法、重写方法、抽象方法和方法重载
- 虚方法:用
virtual
修饰,调用虚方法时,为其调用方法的实例运行时类型决定了要调用的实际方法实现代码 - 重写方法:可以在派生类中重写虚方法,当实例方法中包含
override
时,该方法将重写具有相同签名的继承的虚方法 - 抽象方法:无实现的虚方法,
abstract
修饰,只允许在也声明abstract的类中使用,必须在所有非抽象派生类中重写抽象方法。抽象类专门作为基类来使用,具有解耦功能。 - 方法重载:同一类中可以有多个同名方法,只要这些方法具有唯一签名即可
9. 属性
属性是一种用于访问对象或类型的特征的成员,特征反映了状态。属性是字段的自然扩展。
ctrl + e+r自动生成属性或者右键->Refactor->Encapsulate Field->可以修改你想要的属性的名称->Okay
class Test
{private int age;public int Age{get { return age; }set{if (value > 0 && value <= 120){age = value;}else{throw new Exception("Age value has error");}}}
}
10. 索引器
索引器使对象能够用与数组相同的方式(即使用下标)进行索引。
索引器可以重载。
11. 默认值
以下类别的变量会自动初始化为其默认值
- 静态变量
- 类实例的实例变量
- 数组元素
六. 结构
借助类,两个变量可以引用同一个对象,对一个变量执行的运算可能会影响另一个变量引用的对象;借助结构,每个变量都有自己的数据副本,对一个变量执行的运算不会影响另一个变量。
Point a = new Point(10, 10);
Point b = a;
a.x = 20;
//若Point为类,输出20,Point为结构,输出10
Console.WriteLine(b.x);
七. 数组
数组是引用类型,声明数组变量只是为引用数组实例预留空间,实际的数组实例是在运行时使用new创建。
C#允许"@"字符作为前缀以使关键字能够作为标识符
八. vs使用技巧
鼠标放在空行
,右键单击并选择快速操作和重构
菜单,就可以生成构造函数之类的。
ctrl+k+d:调整格式
ctrl + d:复制当前行到下一行