快捷操作
自动补全
写出没有using的方法,使用Alt+Enter
选择确定的选项进行补全。
程序结构
一个 C# 程序主要包括以下部分:
- 命名空间声明(Namespace declaration)
- 一个 class
- Class 方法
- Class 属性
- 一个 Main 方法
- 语句(Statements)& 表达式(Expressions)
- 注释
例如下面这个例子
using System;
namespace HelloWorldApplication
{
class HelloWorld
{
static void Main(string[] args)
{
/* 我的第一个 C# 程序*/
Console.WriteLine("Hello World");
Console.ReadKey();
}
}
}
字符串
对应数据类型:string
在这里使用WriteLine函数对字符串进行处理并输出到屏幕
-
含变量输出
string friend = "fozen"; //第一种 Console.WriteLine("Hello"+friend); //第二种 //$表示后面的字符串有需要转义的内容 Console.WriteLine($"Hello {friend}");
-
删除空格
string friend = " fozen "; string friend1 = friend.Trim(); //删除开头和结尾的空格 string friend2 = friend.TrimStart(); //删除开头的空格 string friend3 = friend.TrimStart(); //删除结尾的空格
-
长度
string friend = "fozen"; int friendLength = friend.Length;
var
从.NET 3.0开始,在方法内部可以使用var关键字声明局部变量。
var关键字用来隐式地声明一个数据类型,变量类型是在编译期确定的,而不是在运行时确定的
例如声明一个var变量a,把整型数10赋值给a,接着把一个字符串赋值给变量a。运行发现报错:无法将类型string隐式转换为int。
var的作用:
-
var关键字让编码更简短
var obj = new HelloIHaveALongLongName<string, string>();
类
在这里举个类的例子
class Box{
private double length;
public Box(){}
public Box(double len){
length = len;
}
~Box(){}
public void setLength(double len){
length = len;
}
public double getLength(){
return length;
}
}
可以看出,C#也是有构造函数和析构函数的,并且析构函数前面不能有任何前缀。
get和set
这是对对象成员变量进行修改的关键字
例如
class Box{
private string name;
public string Name{
get{return name;}
set{name = value;}
}
}
class exec{
static void Main(string[] args){
Box box = new Box();
box.Name = "Tom";
Console.WriteLine(box.Name);
}
}
这里value
是直接被指定赋进来的值的
也可以加入判断条件
class Box{
public string name;
public string Name{
get{return name;}
set{
if(value != "") name = value;
else name = "NULL";
}
}
}
也可以简化地更简单:
class Box{
private string name;
private string Name{get;set;}
}
get和set会自动识别函数对应的成员变量name
继承
C#中继承的格式如下:
class <派生类> : <基类>
{
...
}
- 冒号后写基类不带任何权限属性,像public之类的这些
- C#不支持多重继承,可以使用接口来实现多重继承
多态
C#多态分为静态多态和动态多态。
动态多态是通过抽象类和虚方法实现的。
抽象类:
使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。
- 不能创建一个抽象类的实例。
- 不能在一个抽象类外部声明一个抽象方法。
- 抽象类至少有一个未被实现的抽象函数成员。
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
abstract class Shape
{
abstract public int area();
}
class Rectangle: Shape
{
private int length;
private int width;
public override int area ()
{
Console.WriteLine("Rectangle 类的面积:");
return (width * length);
}
}
虚方法:
public class Shape
{
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }
// 虚方法
public virtual void Draw()
{
Console.WriteLine("执行基类的画图任务");
}
}
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("画一个圆形");
base.Draw();
}
}
接口
- 成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
- 接口成员都是且只能是public,所以不用写权限
- 接口成员默认是抽象的,因此不用写abstract
- 接口只能有函数
- 函数只能有声明,不能有实现
- 接口可以继承多个接口,类也可以继承多个接口
格式如下:
interface IMyInterface
{
// 接口成员
void MethodToImplement();
}
接口一定程度上解决了紧耦合的情况,类与类相继承的情况在运行发现值出了问题有时需要排查很久才能知道这个成员变量是在哪个类的定义出了问题(一个类依托继承的类的成员相关联)。而有了接口作为基底,出了问题明显得知具体是哪个类的问题,毕竟接口只有声明没有实现,而类都要实现这个接口的成员函数。
委托
委托类似于函数指针。
使用如下:
-
声明委托
delegate T MyDele<T>(T a, T b);
使用了泛型,对应该类型的方法传入的参数和return类型。
-
创建方法(与实例化委托处于同一类中需要加静态关键字,不在同一个类可以不加)
static int Add(int x ,int y){return x+y;}
-
实例化委托
MyDele<int> deleAdd = new MyDele<int>(Add);
-
输出查看结果
Console.WriteLine(deleAdd.InVoke(1,2)); // 3 // 不加InVoke也行 Console.WriteLine(deleAdd(1,2)); // 3
以上是普通的进行委托的步骤,在之后的C#版本进行了更新,可以不用再进行delegate声明委托了,取而代之的是Action和Func
Action是针对返回值为void的函数
static void Main(string[] args)
{
Action<string> action = new Action<string>(PrintName);
action("tom");
}
public static void PrintName(string name)
{
Console.WriteLine($"name : {name}");
}
Func是针对有返回值的函数
static void Main(string[] args)
{
Func<int, int, int> func = new Func<int, int, int>(Add);
int ret = Add(1, 2);
Console.WriteLine($"ret:{ret}");
}
public static int Add(int num1,int num2)
{
return num1 + num2;
}
泛型中最后一个int对应返回值
反射
C#编写的程序会编译成一个程序集(.DLL或.exe),其中会包含元数据、编译代码和资源,通过反射可以获取到程序集中的信息
通俗来讲,反射就是我们在只知道一个对象的外部而不了解内部结构的情况下,可以知道这个对象的内部实现
反射是依赖注入实现的关键操作
反射的优缺点
首先在编译中分为动态编译和静态编译,静态编译是在编译中确定类型,绑定对象,而动态编译是在运行中确定类型,绑定对象
反射的优点就是可以动态创建对象、绑定对象,提高了程序的灵活性和扩展性,但反射是一种解释操作,在性能上不如静态编译快