来源:
菜鸟教程

C#是唯一彻头彻尾为.NET Framework 设计的语言

结构:

using System; //引入命名空间
namespace ConsoleApp1 //namespace 关键字声明,就是声明在同一个程序集里面的意思。ConsoleApp1命名空间,不用和创建的项目一样
{
    class pro 
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello word!");
            Console.ReadKey();
        }
    }
}

其中:

namespace ConsoleApp1{
    class P1{}
}

namespace ConsoleApp1;
class P2{}

P1与P2是属于同一个命名空间

数据类型

1、值类型

就是常见的bool,byte,int,long 等

2、引用类型

引用类型的变量中存储的是内存的位置

// 对象类型(object)
object obj;
obj = 10;
Console.WriteLine(obj);

obj = "hello";
Console.WriteLine(obj);

// 动态类型(dynamic)
dynamic d = 20;
Console.WriteLine(d);

d = "hello";
Console.WriteLine(d);
        
// 动态类型与对象类型类似,但对象类型变量的类型检查是在编译时进行的,而动态类型变量的类型检查则是在程序运行时进行的。
        
// 字符串类型(String)
String str1 = "Hello Word!";
String str2 = @"Hello Word"; // @作用相当于python中的 r

4、指针类型

C# 中的指针与 C 或 C++ 中的指针具有相同的功能

ps:引用类型与指针类型相似,引用类型安全性更高,可以视为是一个托管的指针,dotnet环境管理着,如果没有“指针”指向这个空间,则会在合适的时候释放

变量

接受用户输入的值并存储到变量中:

Console.WriteLine("请输入一个数字和字符串:");
a = Convert.ToInt32(Console.ReadLine());
b = Convert.ToString(Console.ReadLine());
Console.WriteLine("{0} and {1}", a, b);

if else

a = Convert.ToInt32(Console.ReadLine());
if (a % 2 == 0) Console.WriteLine("偶数");
else if(a==1) Console.WriteLine("为1");
else Console.WriteLine("非1奇数");

switch语句

 public void switchFunc()
    {
        a = Convert.ToInt16(Console.ReadLine());
        switch (a/2)
        {
            case 2: 
                Console.WriteLine(2);
                break;
            case 6: 
                Console.WriteLine(6);
                break;
            case 9:
                Console.WriteLine(9);
                break;
            default:
                Console.WriteLine("other");
                break;
        }
    }

for循环

public void forFunc()
    {
        for (int i = 0; i < 9; i++)
        {
            Console.Write("{0} ",i);
        }
    }

while循环

while(表达式){
    循环主体;         // 要执行的代码
}
do{
    循环主体;        // 要执行的代码
}while(表达式);

foreach

遍历数组或集合对象中的每一个元素

public class P7
{
    private int[] arr = new[] { 1, 3, 4, 5, 6, 7 };

    public void foreachFunc()
    {
        foreach (var i  in arr)
        {
            Console.WriteLine(i);
        }
    }
}

相当于python中的for i in arr

跳出循环

1、break

2、continue

3、goto

控制程序跳转到指定模块

public class P8
{
    public void gotoFunc()
    {
        login:
        Console.WriteLine("用户名:");
        string name = Console.ReadLine();
        Console.WriteLine("密码:");
        string pwd = Console.ReadLine();
        if (name == "lzz" && pwd == "123456")
        {
            Console.WriteLine("登录成功!");
        }
        else
        {
            Console.WriteLine("密码错误重新登录!");
            goto login;
        }
    }
}

using用法

1、using指令

using 命名空间名字,相当于Python的import

2、using 别名

using 别名=包括详细命名空间信息的具体的类型
这种做法有个好处就是当同一个cs引用了两个不同的命名空间,但两个命名空间都包括了一个相同名字的类型的时候。当需要用到这个类型的时候,就每个地方都要用详细命名空间的办法来区分这些相同名字的类型

using aClass = ConsoleApplication1.MyClass;
using bClass = ConsoleApplication2.MyClass;
//后面用到对应的类就使用别名aClass,bClass

3、定义一个范围,在范围结束时处理对象

当在某个代码段中使用了类的实例,而希望无论因为什么原因,只要离开了这个代码段就自动调用这个类实例的Dispose

(Dispose() 方法的调用并不会销毁对象本身,而只是释放对象持有的资源)

C#函数

C# 中的静态函数指的是,在一个类中使用 static 修饰的函数,调用静态函数比调用普通函数要简单很多,只需要函数名即可。

C#封装

1、public

可以从类外部访问类中的公共成员

2、private

类外不可以访问类内私有成员,即使是该类对象也不行

3、protected

子类中可以访问父类中protected修饰的成员

4、internal

internal修饰的成员可以被同一个命名框架下的任何类或方法访问

5、protected internal

可以在自身类、派生类或同一命名空间的程序集中被访问

参数传递

1、值传递

public void send1(int val)
    {
        val *= val;
        Console.WriteLine("函数内部值:{0}",val);
    }

/*
调用函数之前:10
函数内部值:100
调用函数之后:10
*/

2、引用传递 ref

内部的val改变,外部的val也改变

public void send2(ref int val)
    {
        val *= val;
        Console.WriteLine("函数内部值:{0}",val);
    }
/*
调用函数之前:10
函数内部的值:100
调用函数之后:100
*/

3、输出传递 out

return只能返回一个值,而输出传递可以返回多个值,与引用传递类似,不同的是输出传递是将数据从函数中传输出来,而不是传输到函数中。

public void send3(out int x)
    {
        int temp = 11;
        x = temp;
        x *= x;
    }

可空类型

data_type? variable_name = null;

声明时需要加上 '?' 才可以赋值null

num = null ?? 321  //num=321
num = 123 ?? 任意数(包括null)  //num=123

数组

int[] array1; //声明
array1 = new int[10]; //初始化

二维数组

// 第一种方式
int[,] arr = new int[3,4]{
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11}
};
// 第二种方式
int[,] arr = new int[,]{
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11}
};
// 第三种方式
int[,] arr = {
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11}
};

// 访问
arr[2,3]

交错数组

交错数组是数组中每个元素都可以是维度和大小不同的数组

int[][] jaggedArray = new int[3][]; //声明

//初始化
jaggedArray[0] = new int[5]{1,2,3,4,5};
jaggedArray[1] = new int[4]{1,2,3,4};
jaggedArray[2] = new int[2] { 1, 2 };

//访问
jaggedArray[0][2] 

//元素是二位数组时:jaggedArray[1][1,1] 

参数数组 params

参数数组通常用于为函数传递未知数量的参数

public void getSum(params int[] arr)
    {        
    }
//参数数组与直接传递数组的区别:
public void getSum2(params int[] arr)
{   int sum = 0;
    arr[1]=2;
    foreach(var i in arr) sum+=i;
    Console.WriteLine("数组参数函数内部计算的数组总和:{0}",sum);
}

getSum2(5,6,7);

Array

Array有多个方法,需要再查询

public void ArrFunc()
    {
        int[] arr = new int[6]{15, 33, 29, 55, 10, 11 };
        Console.WriteLine(arr.Length); //获取长度
        
        Array.Sort(arr); //排序
        foreach (var a in arr)
        {
            Console.Write("{0} ", a);
        }

        Console.WriteLine(Array.IndexOf(arr, 33)); //查找索引
        
    }

String

String属性:Chars[Int32],Length
String方法:多个,用到直接查

结构体

class P1
    {
        struct Books
        {
            public string title;
            public int book_id;

            public void strFunc()
            {
                Console.WriteLine("这是结构体里面的函数!");
            }
        }
        public void strucrFunc()
        {
            Books book = new Books();
            book.title = "C#入门";
            book.book_id = 110;
            Console.WriteLine(book.title);
            Console.WriteLine(book.book_id);
            book.strFunc();
        }
    }

枚举类型enum

enum里面每个值都表示一个整数值(也可以理解为变量)

internal class P2
    {
        enum Day { S,M=4,T};
        public void enumFunc()
        {
            Console.WriteLine((int)Day.S);
            Console.WriteLine((int)Day.M);
            Console.WriteLine((int)Day.T);
        }
    }
//0 4 5

Student Object3 = new Student();
Student Object2 = Object3;

Object3有任何变动都会影响到Object2的。

构造函数

1、实例构造函数

构造函数名称与所在类名称相同,没有返回值,使用new创建类对象时,可以使用实例构造函数创建和初始化类中任意成员属性

2、静态构造函数

特点:
1、不使用访问权限修饰或不具有参数
2、类或结构体只能有一个
3、不能继承或重载
4、不能直接调用,仅恶意由公共语言运行时(CLR)调用
5、用户无法控制程序中静态构造函数执行时间
6、创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数以初始化类
7、静态构造函数会在实例构造函数之前运行

3、私有构造函数

如果一个类中具有一个或多个私有构造函数而没有公共构造函数的话,那么其他类(除嵌套类外)则无法创建该类的实例。

好处就是空构造函数可阻止自动生成无参数构造函数。

class P3
    {
        private P3() { }
        public static int id;
        public static string name;
        public static void Display()
        {
            Console.WriteLine("姓名:" + name + " 编号:" + id);
        }
    }

调用时:

// P3 stu = new Student();
        P3.id = 101;
        P3.name = "张三";
        P3.Display();

注释取消,程序出错,因为私有构造函数无法在外部访问

析构函数

主要用于在垃圾回收器回收类实例时执行一些必要的清理操作(在删除对象之前执行一些清理操作)。

特点:
1、只能在类中不能在结构体中
2、一个类定义一个
3、不能继承或重载
4、没有返回值
5、自动调用
6、不能使用访问权限修饰,不能包含参数
7、函数名与类名一致,前面加上一个~

this关键字

1、表示当前类对象

this.属性(不加this也可以)

2、this关键字串联构造函数

class P4
    {
        public P4()
        {
            Console.WriteLine("无参构造函数");
        }
        public P4(string texts):this()
        {
            Console.WriteLine(texts);
            Console.WriteLine("有参构造函数");
        }
    }

这样会先执行无参然后执行有参

3、使用this关键字作为类的索引器

索引器(Indexer) 允许一个对象可以像数组一样使用下标的方式来访问,类似属性。
(可以理解为一个数组的属性,然后在访问和赋值直接用 实例[index]就行,而不用 实例.数组属性[index] 目前就这个理解,变成了用索引获取属性)

public class MyClass
{
    private int[] arr = new int[100];

    // 索引器定义
    public int this[int i]
    {
        get { return arr[i]; }
        set { arr[i] = value; }  //value关键字表示要赋给索引器所代表的元素的值
    }
}

MyClass obj = new MyClass();
obj[0] = 1;  // 使用索引器设置值
int value = obj[0];  // 使用索引器获取值

4、使用this关键字作为原始类型的扩展方法

static class P5
    {
        public static string thisFunc(this string name)
        {
            return name + " hello!";
        }
    }

调用

Console.WriteLine("Cedar".thisFunc());

静态成员 static

public class MyClass {
    public static int myVar;  // 这是一个静态变量
    public static void MyMethod() {   // 这是一个静态方法
        // ...
    }
}
MyClass.myVar = 10;  // 访问静态变量
MyClass.MyMethod();  // 调用静态方法

1、静态属性

静态属性可以直接通过类名.属性名的形式直接访问,不需要事先创建类的实例。静态属性不仅可以使用成员函数来初始化,还可以直接在类外进行初始化。

2、静态函数

使用 static 定义的成员函数称为“静态函数”,静态函数只能访问静态属性

3、实例成员与静态成员的区别

  • 实例成员属于一个类的每个实例,每个实例都有自己的数据副本。
  • 静态成员属于类本身,所有实例共享同一份数据。
  • 实例成员需要通过类的实例进行访问,而静态成员可以直接通过类名进行访问,不需要创建实例。

继承

class 派生类 : 基类{
    ... ...
}
namespace ConsoleApp1
{
    //基类
    class P6
    {
        protected int width, height;
        public void setWidth(int w)
        {
            width = w;
        }
        public void setHeight(int h)
        {
            height = h;
        }
    }

    //派生类
    class kidP6 : P6
    {
        public int getArea()
        {
            return width*height;
        }
    }
}

2、多重继承

多重继承则是指一个类可以同时继承多个基类,C# 并不支持多重继承,但是可以借助接口来实现多重继承。

多态

1、编译时多态

函数多态:函数同名,彼此间有差异,比如参数个数不同或参数类型不同等等,返回值类型不同除外。

2、运行时多态

常量

常量是在编译时定义的值,在应用程序的生命周期内不会更改。关键字使用 const 访问修饰符和变量类型声明常量。然后,从类外部,可以使用类的名称访问常量值,并且只能使用类内部的常量名称访问常量值。

常量在编译时就已经确定其值,并且可以直接使用,不需要实例化对象。

public const int MaximumYearsEnrolled = 5;

记录 record

public record StudentRecord (string FirstName, string LastName, int Age); //这是一个类 record就是快捷生成ToString()函数而已

//调用
var s = new StudentRecord("Sally", "Smith", 30);
s.toString()

魔术参数和可选参数

以在方法中使用一个特殊的params 关键字,该关键字将捕获传入该方法的任何参数

public class P16
{
    public decimal CalAvg(
        int yearOfStudy,
        params string[] classes
    )
    {
        foreach (var c in classes)
        {
            Console.WriteLine("Calculating for class " + c);
        }

        return 9;
    }
}

调用:

var s = new P16();
            s.CalAvg(2020, "Algebra", "History", "Computer Science", "Art", "English", "German", "Gym", "Study Hall");

可以看作是一个不定长数组

接口

1、声明接口

public interface InterfaceName{
    returnType funcName1(type parameterList);
    returnType funcName2(type parameterList);
    ...
}

2、接口继承

一个接口可以继承另一个接口,例如可以使用接口 1 继承接口 2,当用某个类来实现接口 1 时,必须同时实现接口 1 和接口 2 中的所有成员

namespace ConsoleApp1;

// 接口1
public interface IParentInterface
{
    void ParentInterfaceMethod();
}
    
// 接口2继承接口1
public interface IMyInterface : IParentInterface
{
    void MethodToImplement();
}
// 实现接口的类
public class P11 : IMyInterface
{
    public void MethodToImplement()
    {
        Console.WriteLine("实现接口2中的方法");
    }

    public void ParentInterfaceMethod()
    {
        Console.WriteLine("实现接口1中的方法");
    }
}

命名空间

就是一个范文,可以理解为一个包

1、定义命名空间

namespace namespaceName{
    // 命名空间中的代码
}

也可直接用

namespace namespaceName;

2、using关键字

using 关键字用来引用指定的命名空间,它可以告诉编译器后面的代码中我们需要用到某个命名空间。

就有点像python中import代码库。

3、命名空间嵌套

namespace namespaceName1{
    // namespaceName1 下的代码
    namespace namespaceName2{
        // namespaceName2 下的代码
    }
}

可以使用点.运算符来访问嵌套的命名空间成员

using First.Second;

预处理指令

预处理器指令不是语句,因此它们不需要以分号;结尾

C# 中,预处理指令用于帮助条件编译。不同于 C 和 C++ 中的指令,在 C# 中不能使用这些指令来创建宏,而且预处理器指令必须是一行中唯一的代码,不能掺杂其它。

1、define

#define 预处理器指令用来创建符号常量,这个符号可以作为传递给 #if 指令的表达式,表达式将返回 true。

2、if条件指令

#if 来创建条件指令,条件指令可以用于测试一个或多个符号的值是否为 true 。如果符号的值为 true,那么编译器将评估 #if 指令和下一个指令之间的所有代码。在语法上 #if 预处理器语句与 C# 中的 if 条件判断语句比较相似

public class P12
{
    public void defineAndIfFunc()
    {
        #if(PI)
            Console.WriteLine("PI已定义");
        #else
            Console.WriteLine("PI 未定义");
        #endif
    }
}

正则表达式

占位,跟其他语言一样

异常处理

1、try/catch语句

没什么好说的,就是try catch finally

自定义异常类

除了可以使用系统预定义的异常类外,我们还可以自行定义异常类,自定义的异常类都应继承 System.ApplicationException 类

using System;
namespace ConsoleApp1;

public class InvalidAgeException : ApplicationException
{
    public InvalidAgeException (string message)
    {
        Console.WriteLine(message);
        Console.WriteLine("自定义异常!");
    }
}
public class P13
{
    public void validate(int age)
    {
        if(age < 18)
        {
            throw (new InvalidAgeException("Sorry, Age must be greater than 18"));
        }
    }
}

抛出异常

catch(Exception e) {
   ...
   Throw e
}

C# 文件读写

占位

目录操作

占位

C#特性

[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public sealed class DeprecatedAttribute : Attribute
{
    private string reason;

    public DeprecatedAttribute(string reason)
    {
        this.reason = reason;
    }

    public string Reason => reason;
}


public class MyClass
{
    [Deprecated("This method is deprecated, use NewMethod instead.")]
    public void OldMethod()
    {
        Console.WriteLine("Old Method");
    }
    
    [Deprecated("This method is New.")]
    public void NewMethod()
    {
        Console.WriteLine("New Method");
    }
}


var methods = typeof(MyClass).GetMethods();

foreach (var method in methods)
{
    var attributes = method.GetCustomAttributes(typeof(DeprecatedAttribute), false);

    foreach (DeprecatedAttribute attr in attributes)
    {
        Console.WriteLine($"Method {method.Name} is deprecated: {attr.Reason}");
    }
}

// 输出: 
// Method OldMethod is deprecated: This method is deprecated, use NewMethod instead.
// Method NewMethod is deprecated: This method is New.

可以直接理解成通过被修饰的 属性/方法/类 访问另外一个类的属性值

AttributeUsage特性用于描述自定义特性的使用约束:

1、AttributeTargets 枚举定义了可以应用特性的程序元素的类型。这里有一些可用的值:

  • AttributeTargets.Class:特性可以应用于类。
  • AttributeTargets.Struct:特性可以应用于结构体。
  • AttributeTargets.Enum:特性可以应用于枚举。
  • AttributeTargets.Constructor:特性可以应用于构造函数。
  • AttributeTargets.Method:特性可以应用于方法。
  • AttributeTargets.Property:特性可以应用于属性。
  • AttributeTargets.Field:特性可以应用于字段。
  • AttributeTargets.Event:特性可以应用于事件。
  • AttributeTargets.Interface:特性可以应用于接口。
  • AttributeTargets.Delegate:特性可以应用于委托。
  • AttributeTargets.Assembly:特性可以应用于程序集。

2、ValidOn:这个参数必须是AttributeTargets枚举的一个或多个值,表示这个特性可以应用于哪些程序元素

3、Inherited:这是一个布尔值,表示如果派生类没有该特性,则是否从基类继承特性。默认值为 true

4、AllowMultiple:这是一个布尔值,表示特性是否可以多次应用于同一程序元素。默认值为 false

C#反射

占位

C#属性

属性(Property)是类(class)、结构体(structure)和接口(interface)的成员,类或结构体中的成员变量称为字段,属性是字段的扩展,使用访问器(accessors)可以读写私有字段的值。

1、访问器

属性访问器有两种,分别是 get 属性访问器和 set 属性访问器。

public class P14
{
    private string code = "N-K";
    
    //声明类型为string的code
    public string Code
    {
        get
        {
            return code;
        }
        set
        {
            code = value;
        }
    }
    // override覆盖的意思
    public override string ToString()
    {
        return "编号 = " + Code;
    }
}

调用:

P14 p14 = new P14();
Console.WriteLine(p14);
p14.Code = "sf";
Console.WriteLine(p14);

问题:跟直接public有什么区别呢?

2、抽象属性

抽象类中可以拥有抽象属性,这些属性会在派生类中实现

索引器

索引器能够让对象以类似数组的形式来操作。

索引器与属性类似,也用到get和set,区别是属性不需要提供参数,索引器需要。

namespace ConsoleApp1;

public class P15
{
    static public int size = 10;
    private string[] namelist = new string[size]; //注意这个是private

    public P15()
    {
        for (int i = 0; i < size; i++)
        {
            namelist[i] = "NULL";
        }
    }
    
    //索引器
    public string this[int index]
    {
        get
        {
            string tmp;
            if (index >= 0 && index < size)
            {
                tmp = namelist[index];
            }
            else
            {
                tmp = "无";
            }

            return (tmp);
        }
        set
        {
            if (index >= 0 && index <= size - 1)
            {
                namelist[index] = value;
            }
        }
    }
    
}

调用:

P15 p15 = new P15();
p15[0] = "a";
p15[1] = "b";
p15[2] = "c";
for ( int i = 0; i < P15.size; i++ ){
     Console.WriteLine(p15[i]);
}

2、索引器重载

索引器可以被重载,而且在声明索引器时也可以带有多个参数,每个参数可以是不同的类型。另外,索引器中的索引不必是整数,也可以是其他类型

委托

1、自定义

//delegate即可以是定义委托,也可以是定义匿名函数
delegate int NumberChanger(int n); //声明一个委托。编译器会将委托的定义编译成一个类
namespace ConsoleApp1
{
    class P7
    {
        static int num = 10;

        // 方法3
        public static int GetNum()
        {
            return num;
        }

        // 方法1
        public static int AddNum(int p)
        {
            num += p;
            Console.WriteLine(GetNum());
            return num;
        }
        // 方法2
        public static int MultNum(int q)
        {
            num *= q;
            Console.WriteLine(GetNum());
            return num;
        }
        

        // 使用委托
        public void MainFunc()
        {
            //创建委托实例
            NumberChanger nc = new NumberChanger(AddNum);
            // 多播委托(合并委托)
            nc += MultNum;
            nc(5);
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        //其实委托就类似于数据类型,只是变量指向的是方法而已
        D1 d = P1;
        d += P2;  
        d();  //多播委托,调用时依次调用列表中的委托
    }
    static void P1()
    {
        Console.WriteLine("我是P1");
    }
    static void P2()
    {
        Console.WriteLine("我是P2");
    }
}

//委托与类平级
delegate void D1();

委托的参数n就会是函数参数里面的p值。

除了自己定义的委托外,系统提供一个内置委托类型,Action和Func

Action委托

Action委托引用了一个void返回类型 T表示方法参数
Action
Action<int T1, int T2, ...,T16>

Func委托

Func<out TResult>
Func<int T1,int T2,...,T16, out TResult>

事件

事件需要在类中声明和触发,并通过委托与事件处理程序关联。

发布器确定何时触发事件,订阅器确定对事件作出何种响应;

namespace ConsoleApp1
{
    class P8
    {
        public void mainFunc()
        {
            PublisherDemo e = new PublisherDemo();//实例发布器类
            SubscriberDemo v = new SubscriberDemo();//实例订阅器类
            e.myEvent += new PublisherDemo.myEntrust(v.printf);//委托
            e.SetValue("Cedar");
        }
    }
    // 发布器类
    public class PublisherDemo
    {
        private string value;
        public delegate void myEntrust(string str); //委托
        public event myEntrust myEvent; //基于委托定义事件
        public void SetValue(string str)
        {
            value = str;
            myEvent(value); //触发事件
        }
    }

    // 订阅器类:触发后的反应
    public class SubscriberDemo
    {
        public void printf(string str)
        {
            Console.WriteLine(str);
        }
    }
}

C#中的集合

1、动态数组 ArrayList

namespace ConsoleApp1
{
    public class P9
    {
        public void mainFunc()
        {
            ArrayList arr_list = new ArrayList(); ;
            Console.WriteLine("输入N1,N2,N3...");
            string str = Console.ReadLine();
            string[] strArray = str.Split(',');
            foreach (string s in strArray)
            {
                arr_list.Add(s);
            }

            Console.WriteLine("获取或设置动态数组中可以包含的元素个数:{0}", arr_list.Capacity);
            Console.WriteLine("数组长度:{0}",arr_list.Count);

            Console.Write("排序后的数组:");
            arr_list.Sort();
            foreach (string s in arr_list)
            {
                Console.Write(s + " ");
            }
        }
    }
}

2、哈希表 Hashtable

namespace ConsoleApp1
{
    class P10
    {
        public void mainFunc()
        {
            Hashtable ht = new Hashtable();
            ht.Add("1", "小明");
            ht.Add("2", "小红");
            ht.Add("3", "小黄");

            if (ht.ContainsValue("小明"))
            {
                Console.WriteLine("该学生名存在");
            }
            else
            {
                ht.Add("4", "张三");
            }

            ICollection key = ht.Keys;
            foreach (string i in key)
            {
                Console.Write(i+" : " + ht[i]);
            }
        }
    }
}

3、排序列表 SortedList

SortedList 类用来表示键/值对的集合,这些键/值对按照键值进行排序,并且可以通过键或索引访问集合中的各个项

namespace ConsoleApp1
{
    internal class P11
    {
        public void mainFunc()
        {
            SortedList s = new SortedList();
            s.Add("001", "小明");
            s.Add("002", "小红");
            s.Add("003", "小强");
            if (s.ContainsValue("张三"))
            {
                Console.WriteLine("该学生姓名以存在");
            }
            else
            {
                s.Add("004", "张三");
            }
            // 获取键的集合
            ICollection key = s.Keys;
            foreach (string k in key)
            {
                Console.WriteLine(k + ": " + s[k]);
            }
        }
    }
}

就是一个按key值排序的哈希表

4、堆栈 Stack

namespace ConsoleApp1
{
    internal class P12
    {
        public void mainFunc()
        {
            Stack st = new Stack();

            st.Push(1);
            st.Push(2);
            st.Push(3);

            Console.WriteLine("当前堆栈中元素:");
            foreach (var x in st) Console.Write(x + " ");
            Console.WriteLine();

            st.Push("A");
            Console.WriteLine("下一个弹出的值:{0}",st.Peek());
            Console.WriteLine("当前堆栈中的元素:");
            foreach (var c in st)
            {
                Console.Write(c + " ");
            }
            Console.WriteLine();

            Console.WriteLine("删除值下面的值:");
            Console.Write(st.Pop() + " ");
            Console.Write(st.Pop() + " ");
            Console.Write(st.Pop() + " " + "\r\n");

            Console.WriteLine("当前堆栈中的元素:");
            foreach (var c in st)
            {
                Console.Write(c + " ");
            }
        }
    }
}

5、队列 Queue

q.Enqueue('A');
char ch = (char)q.Dequeue();

6、点阵列 BitArray

BitArray 类用来管理一个紧凑型的位值数组,数组中的值均为布尔类型,其中 true(1)表示此位为开启,false(0)表示此位为关闭。

地图标记可以用这个

泛型 Generic

就是未确定数据类似,所以先占个位置,类似于var的功能

//定义泛型类
    class GenericClass<T>
    {
        public GenericClass(T msg)
        {
            Console.WriteLine(msg);
        }
    }

//使用
GenericClass<int> int_gen = new GenericClass<int>(1234567);
            GenericClass<char> char_gen = new GenericClass<char>('C');
//定义泛型方法
static void Swap<T>(ref T lhs, ref T rhs)
        {
            T temp;
            temp = lhs;
            lhs = rhs;
            rhs = temp;
        }

//使用
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
//泛型委托
delegate T NumberChanger<T>(T n);

//使用
NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);

匿名函数

就是没有函数名,只有函数体的函数,返回类型从方法体内的return中推断的

匿名函数是通过使用 delegate 关键字创建的委托实例来声明的

就是说是通过委托来使用匿名函数的

//委托
delegate void NumberChanger(int n);

//匿名函数
NumberChanger nc = delegate(int x)
{
Console.WriteLine("匿名函数: {0}", x);
};
//使用匿名函数
nc(10);

指针变量与unsafe

C#默认不支持指针,但如果使用unsafe修饰类或类中的成员,这段代码视为不安全,C#允许不安全代码中使用指针

Lambda表达式

Lambda就是一个匿名函数
(参数列表)=>表达式或语句块

合法的Lambda表达式

1、(x, y) => x * y                    //多参数,隐式类型=> 表达式  
2、x => x * 5                        //单参数, 隐式类型=>表达式  
3、x => { return x * 5; }            //单参数,隐式类型=>语句块  
4、(int x) => x * 5                    //单参数,显式类型=>表达式  
5、(int x) => { return x * 5; }     //单参数,显式类型=>语句块  
6、() => Console.WriteLine()        //无参数

扩展方法

扩展方法本质上是静态方法,需要放在静态类中。扩展方法就是不修改类型,不用继承,也可以添加新方法。
扩展方法用对象调用,看起来就像是这个对象所拥有的。

public static class MyExtend
{
    public static void Print(this string str) //用this修饰数据类型,这样有string类型的对象都可以调用这个扩展方法
    {
        Console.WriteLine(str);
    }
}


public class P17
{
    public void mainFunc()
    {
        string s = "Hello world!"; //对象方式调用
        s.Print();
        MyExtend.Print(s); //静态方式调用
    }
}

如果扩展方法与类型中原有方法同名,调用原有方法

typeof运算符

是一个运算符不是一个函数,返回的是一个System.Type对象

Type t = typeof(SomeClass);
class Program
{
    static void Main(string[] args)
    {
        Persion p = new Persion();
        Type t1 = typeof(Persion); //通过类名称获得类的信息
        Type t2 = p.GetType(); //通过object方法获得类的类型

        FieldInfo[] fileds = t1.GetFields(); //获取所有的属性
        foreach (var item in fileds)
        {
            Console.WriteLine(item);
        }
        
        MethodInfo[] filedsm = t1.GetMethods(); //获取所有的方法
        foreach (var item in filedsm)
        {
            Console.WriteLine(item);
        }
    }

    class Persion
    {
        public int age;
        public string name;

        public void ShowName()
        {
            Console.WriteLine(name);
        }
    }
}

C#命名规范

https://www.jianshu.com/p/dc26cb8ffcb9

1、单词的首字母大写命名属性、方法、事件和类名
2、第一个单词的首字母不大写命名成员变量、局部变量和方法的参数
3、接口的名称一般以大写I作前缀
4、自定义的属性以Attribute结尾
5、自定义的异常以Exception结尾
6、类名用名词或名词短语来命名
7、方法的命名一般将其命名为动宾短语
8、布尔型变量或者方法一般可以用is、can、has或者should做前缀
9、判断条件是一个布尔变量时不要使用==进行条件判断
10、私有变量用_开头

备注:
OOP语言:面向对象语言

最后编辑:2023年08月16日 ©著作权归作者所有