C#编程扩展

泛型

泛型允许我们编写类和方法中的数据类型,直到真正使用时确定数据类型的规范。

泛型是延迟编写数据类型。

结构:类名或方法名等< 泛型名称 >  泛型名称是可以随意定义的,但在有多个泛型名称时会相互关联 。

泛型名称通常作为变量数据类型(int ,string 等),实际上任何任何有效名都可以。

泛型无法直接使用运算符;泛型名称当数据类型时,是会延迟编写的,需要在类创造对象,也就是实例时定义<>中传入的数据类型。

泛型有:泛型类,泛型方法,泛型接口,泛型集合,泛型委托,泛型事件

泛型提高了代码性能,避免了装箱和拆箱。装箱和拆箱都会克隆占内存

int a=10; 

object ob = a;//这是装箱

int b = (int)ob;//这是拆箱

泛型类

你可以定义一个泛型类,该类可以接收一个或多个类型参数。这些类型参数在类的定义中被用作占位符,并在创建类的实例时被具体的类型所替代。

public class GenericClass<T>  
{  
    private T value;  
  
    public GenericClass(T value)  
    {  
        this.value = value;  
    }  
  
    public T GetValue()  
    {  
        return value;  
    }  
}  
  
// 使用泛型类  
GenericClass<int> intClass = new GenericClass<int>(10);  
GenericClass<string> stringClass = new GenericClass<string>("Hello");

泛型方法

泛型方法是在非泛型类中定义的泛型成员。它们允许你在单个方法内部使用泛型类型参数。 

public class NonGenericClass  
{  
    public T GenericMethod<T>(T input)  
    {  
        return input;  
    }  
}  
  
NonGenericClass nonGenericObj = new NonGenericClass();  
int intResult = nonGenericObj.GenericMethod<int>(10);  
string stringResult = nonGenericObj.GenericMethod<string>("Hello");

 泛型接口

类似于泛型类,你也可以定义泛型接口,它可以被任何类型所实现。

public interface GenericInterface<T>  
{  
    T DoSomething(T input);  
}  
  
public class MyGenericClass : GenericInterface<int>  
{  
    public int DoSomething(int input)  
    {  
        return input * 2;  
    }  
}

约束泛型

你可以对泛型类型参数应用约束,以确保它们满足特定的条件(例如继承自特定的基类或实现特定的接口)。

关键字:where

class Class1<T>

where T : struct  //约束为值类型       约束为引用类型用class

{

 internal class Program
 {
     static void Main(string[] args)
     {
         Generics<string,Class> generics = new Generics<string, Class>();

         Generics1<int, string, InterClass<int>, class1, class2> generics1 = new Generics1<int, string, InterClass<int>, class1, class2>();
     }
 }
 public interface InterClass<T>
 {
     T Value { get; set; }
 }
 public class Class:InterClass<int>
 {
     public int Value { get; set; }
 }
 public class Generics<T,R>
 {

     public T Value { get; set; }
     public T Method(T a)
     {
         return a;
     }
 }
 public class class1 { }
 public class class2:class1 { }
 public class Generics1<T,C,I,R,X> 
     //约束泛型
     //约束类型的访问权限都要一样
     where T:struct   //约束为值类型
     where C:class    //约束为引用类型
     where I :InterClass<T>   //约束为继承泛型接口的并实现的子类 或 泛型接口 ,但 T 要传入的类型要一致  
     where R :class1   //约束为子类或父类
     where X :class1,new ()  //约束为子类或父类,且必须有一个无参构造函数
 {
    
     public T Value { get; set; }
     public T Method(T a)
     {
         return a;
     }
 }

集合 

ArrayList集合对应List泛型集合

Hashtable哈希表对应Dictionary字典集合

ArrayList集合  和  Hashtable哈希表  会进行装箱拆箱,占内存

List泛型集合  和  Dictionary字典集合   不会进行装箱拆箱,这俩经常使用

ArrayList集合

ArrayList的优点:

数据存储的数据是动态的, 长度是不固定的。能通过Add和Remove添加和删除元素

可以存储任意类型。它存储的元素都是 object 类型在存储时,会把添加的元素装箱(转换为object类型),访问时,需要拆箱(转换为装箱时对应类型,才能知道该元素是啥),太耗内存不建议使用

它包含于命名空间System.Collections中,需要手动添加或系统自动添加

using System;  
using System.Collections;  
  
class Program  
{  
    static void Main()  
    {  
        // 创建一个 ArrayList 实例  
        ArrayList list = new ArrayList();  
          
        // 添加元素  
        list.Add("Hello");  
        list.Add(123);  
        list.Add(true);  
          
        // 遍历 ArrayList 并打印元素  
        foreach (var item in list)  
        {  
            Console.WriteLine(item);  
        }  
          
        // 获取特定索引的元素(需要显式类型转换)  
        string str = (string)list[0];  
        int num = (int)list[1];  
        bool flag = (bool)list[2];  
          
        // 移除元素  
        list.RemoveAt(1); // 移除索引为 1 的元素  
          
        // 检查 ArrayList 是否包含某个元素  
        if (list.Contains("Hello"))  
        {  
            Console.WriteLine("ArrayList contains 'Hello'.");  
        }  
    }  
}

List泛型集合

格式:List<T> 对象名 = new List<T>();

它位于 System.Collections.Generic 命名空间下。泛型集合提供了类型安全,避免了装箱和拆箱的开销,并且通常比非泛型集合提供更好的性能。

命名空间System.Collections.Generic默认存在

在这里Remove是根据值删除元素

using System;  
using System.Collections.Generic;  
  
class Program  
{  
    static void Main()  
    {  
        // 创建一个 List<object> 实例,但更好的做法是使用具体的类型,如List<string>,List<int>等  
        List<object> list = new List<object>();  
          
        // 添加元素  
        list.Add("Hello");  
        list.Add(123);  
        list.Add(true);  
          
        // 遍历 List 并打印元素(不需要类型转换,但类型仍然是 object)  
        foreach (var item in list)  
        {  
            Console.WriteLine(item);  
        }  
          
        // 获取特定索引的元素(仍然需要显式类型转换,但如果你使用 List<T> 的具体类型,则不需要)  
        string str = (string)list[0];  
        int num = (int)list[1];  
        bool flag = (bool)list[2];  
          
        // 移除元素  
        list.RemoveAt(1); // 移除索引为 1 的元素  
          
        // 检查 List 是否包含某个元素  
        if (list.Contains("Hello"))  
        {  
            Console.WriteLine("List contains 'Hello'.");  
        }  
    }  
}

Hashtable哈希表 

它包含于命名空间System.Collections中,需要手动添加或系统自动添加

在C#中,Hashtable 是一个非泛型的集合,它存储键值对并允许基于键的快速查找。Hashtable 类位于 System.Collections 命名空间中,并且它使用 object 类型来存储键和值。这意味着当你将值放入 Hashtable 时,如果值的类型是值类型(如 intfloat 等),则会发生装箱操作;而在检索时则需要进行拆箱。

Hashtable不保证元素的顺序,即使您尝试按某种顺序添加元素,它也不会保持这种顺序。在用foreach时会不保证元素的顺序。

using System;  
using System.Collections;  
  
class Program  
{  
    static void Main()  
    {  
        // 创建一个 Hashtable 实例  
        Hashtable hashtable = new Hashtable();  
  
        // 添加键值对  
        hashtable.Add("key1", "value1");  
        hashtable.Add(1, "value2"); // 键是 int 类型,会发生装箱  
        hashtable["key3"] = "value3"; // 另一种添加方式  
  
        // 访问值  
        object value1 = hashtable["key1"]; // 返回 object 类型,需要转换或检查类型  
        Console.WriteLine(value1); // 输出: value1  
  
        // 检查键是否存在  
        if (hashtable.ContainsKey("key2"))  
        {  
            Console.WriteLine("key2 exists.");  
        }  
        else  
        {  
            Console.WriteLine("key2 does not exist."); // 输出这个  
        }  
  
        // 遍历 Hashtable  
        foreach (DictionaryEntry de in hashtable)  
        {  
            Console.WriteLine("Key: {0}, Value: {1}", de.Key, de.Value);  
        }  
  
        // 移除键值对  
        hashtable.Remove("key1");  
  
        // 清空 Hashtable  
        hashtable.Clear();  
    }  
}

Dictionary字典集合

字典是包含在命名空间System.Collections.Generic中。

格式:Dictionary<key,value> 对象名 = new Dictionary<key,value> ();

key是键,value是值。键中的每个键是唯一的,键相当于索引,值不是唯一的,键和值是一一对应的。 

键中的元素的数据类型要于key一致,值中的元素的数据类型要于value一致。

优点:可以自己定义索引值

using System;  
using System.Collections.Generic;  
  
class Program  
{  
    static void Main()  
    {  
        // 创建一个 Dictionary<string, int> 实例,其中键是 string 类型,值是 int 类型  
        Dictionary<string, int> dictionary = new Dictionary<string, int>();  
          
        // 添加键值对  
        dictionary.Add("apple", 5);  
        dictionary.Add("banana", 10);  
        dictionary.Add("cherry", 15);  
          
        // 通过键访问值  
        int appleCount = dictionary["apple"];  
        // $可以通过{}直接在字符串中使用变量名等
        Console.WriteLine($"There are {appleCount} apples.");  
          
        // 遍历 Dictionary  
        foreach (var kvp in dictionary)  
        {  
            Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");  
        }  
          
        // 检查 Dictionary 是否包含某个键  
        if (dictionary.ContainsKey("banana"))  
        {  
            Console.WriteLine("The dictionary contains 'banana'.");  
        }  
          
        // 通过键获取值(如果键不存在,将抛出 KeyNotFoundException)  
        try  
        {  
            int orangeCount = dictionary["orange"]; // 这将抛出异常,因为 "orange" 不在字典中  
        }  
        catch (KeyNotFoundException)  
        {  
            Console.WriteLine("The key 'orange' was not found in the dictionary.");  
        }  
          
        // 尝试获取值,如果键不存在则返回默认值  
        int pearCount = dictionary.TryGetValue("pear", out int value) ? value : 0;  
        Console.WriteLine($"There are {pearCount} pears. (Default value used because 'pear' was not found.)");  
          
        // 移除键值对  
        dictionary.Remove("cherry");  
          
        // 再次检查是否包含已移除的键  
        if (!dictionary.ContainsKey("cherry"))  
        {  
            Console.WriteLine("The key 'cherry' has been removed from the dictionary.");  
        }  
    }  
}

委托

委托就是把方法变成引用类型的方式。

关键字:delegate

委托分为定义委托和使用委托

定义委托:委托只能定义在类的内部或命名空间内委托在命名空间中时是全局变量,在类中使用时是局部变量。

委托有访问修饰符,所有修饰符都能使用。

委托可以根据有无返回值,参数数据类型和其长度分为多个委托类型

使用委托:使用委托需要通过创建对象来使用,跟类有点像。

格式:委托名  对象名或变量名  =  new  委托名(方法名);

对象名或变量名:如果是与委托对应的静态方法,才能直接在()中填方法名;非静态是需要通过创建对象来创建的。

方法名:只要方法名,不需要加对应委托的方法后的()。

delegate void Mothod();
delegate int Mothod2();
delegate int Mothod1(int a,int b);
internal class Program
{
    static void Main(string[] args)
    {
        //委托
        
        //赋值
        Mothod mothod = Method;

        //new方法
        Mothod mothod1 = new Mothod(mothod);

        //匿名委托   仅有返回值
        Mothod2 mothod2 = delegate() { return 1; };

        // 使用lambda 匿名方法实例
        //有返回值 有形参
        Mothod1 mothod11 = (int a,int b)=>
        {
            return a + b;
        };
        Mothod1 mothod12 =(a,b)=>a + b;

        //Invoke调用
        mothod11.Invoke(4,6);
        //BeginInvoke
        //这个线程是单独的
        //BeginInvoke开启一个线程去执行委托,NetCore不支持,NetFamework支持  NetCore有更好的多线程功能来支持实现类似功能
        //                      回调方法,状态对象
        mothod11.BeginInvoke(6,4,null,null);
        //EndInvoke
        //BeginInvoke执行完后在执行EndInvoke后面的代码
    }
     static void Method()
 {
     Console.WriteLine("无参无返回值");
 }
}

一个委托可以绑定多个方法

delegate void Delegate();
internal class Delegate2
{
    //委托可以绑定到一个或多个方法上
    public Delegate2()
    {
        Delegate @delegate = new Delegate(Mothod1);
        @delegate += Mothod2;
        @delegate.Invoke();
    }
    public Delegate2(int a)
    {
        Delegate @delegate = Mothod1;
        @delegate += Mothod2;
        @delegate.Invoke();
    }
    public Delegate2(string a)
    {
        
    }

    public void Mothod1() { }
    public void Mothod2() { }
}

泛型委托

格式:委托名<T>  对象名或变量名  =  new  委托名<T>(方法名);

泛型委托可以延迟编辑委托的数据类型,因此可以省去定义多个仅是数据类型不同的委托。

委托也可以跟方法一样作为参数传入方法

Action和Func

Action委托可以代替一个无返回值类型的委托

Func委托可以代替一个有返回值类型的委托

//Action和Func

//有参无返回值   无参无返回值
//Action必须无返回值
Action<int> action1 = new Action<int>(Mothod);
//有参有返回值   无参有返回值
//Func必须有返回值
Func<int, int, string> func = (int x, int y) => { return (x+y).ToString(); };
Func<int> func1 = new Func<int>(Method1);
Console.ReadKey();


public static void Mothod(int a) { }
public static int Method1() { return 1; }

委托的多播

可以将两个相同的类型的委托合并在一起

使用+号合并委托,-号移除委托

static void Main(string[] args)
{
    Program program = new Program();
    Action action;
    Action action1 = new Action(program.Mothod);
    Action action2 = new Action(program.Mothod1);
    //这里只能赋值,不能直接添加
    action = action1;
    //添加
    action += action2;
    //删除
    action -= action1;
    //运行
    action();

    Console.ReadKey();

}
public void Mothod() { }
public void Mothod1() { }

事件 

事件是基于委托的,可以为任何一种委托提供一种发布或订阅机制 。

关键字:event

注意:方法的类型要与事件所对应的委托类型一样

为了应对“事件耦合”。

事件耦合通常指的是不同组件之间通过事件紧密关联,导致它们难以独立测试、修改或重用。

订阅事件:也就是添加方法直接用”+=“。

取消订阅事件:也就是移除方法直接用“-=”。

internal class Program
{   
    Random rnd = new Random(); 
    private static int intMouse;
    static void Main(string[] args)
    {    
             
        Program program = new Program();
        Heater heater = new Heater();
        //订阅事件
        heater.OnHeater += program.Mouse;
        heater.OnHeater += program.Cat;
        heater.OnHeater += program.Cat1;
        heater.OnHeater += program.Person;

        intMouse=program.rnd.Next(1,10);
        //触发事件
        program.MouseWakeUp(heater);
        Console.WriteLine("\t结束");
        //取消订阅事件
        heater.OnHeater -= program.Mouse;
        Console.ReadKey();
    }
    public void MouseWakeUp(Heater heater)
    {
        int intDay = 1;
        while (true)
        {
            int intCat = rnd.Next(10);
            int intPerson = rnd.Next(10);
            if (intCat > 5||intPerson<5)
            {                  
                Console.WriteLine($"\t第{intDay}天");             
                heater.WakeUp();
                if (intPerson == 7)
                {
                    Console.WriteLine("人叫了捕食队,老鼠全死了");
                    break;
                }
                else if (intDay == 7)
                {
                    Console.WriteLine("人叫了捕食队,老鼠全死了");
                    break;
                }
                else if (intCat == 7||intCat==9)
                {    
                    if (intMouse > 0 )
                    {
                        Console.WriteLine("猫抓住了一只老鼠"); 
                        intMouse -= 1;
                        if (intMouse<=0)
                        {
                            break;
                        }
                    }
                    
                }
                else if (intPerson == 3)
                {                     
                    if (intMouse > 0 )
                    {
                        Console.WriteLine("人抓住了一只老鼠");
                        intMouse -= 1;
                        if (intMouse <= 0)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    Console.WriteLine("老鼠跑了");
                }
                intDay += 1;
            }
            else
            {
                Console.WriteLine($"\t第{intDay}天");
                Console.WriteLine("老鼠没来");
                if (intDay == 7)
                {
                    Console.WriteLine("人叫了捕食队,老鼠逃过一劫");
                }    
                intDay += 1;
            }
        }
    }
    public void ZanTing()
    {
        for (int i = 0; i < rnd.Next(3); i++)
        {
            Thread.Sleep(1000);
            Console.Write("");
        }
    }
    public void Cat()
    {
        ZanTing();
        Console.WriteLine("猫醒了");
    }
    public void Cat1()
    {
        ZanTing();
        Console.WriteLine("猫大叫");
    }
    public void Mouse()
    {
        ZanTing();
        Console.WriteLine($"老鼠来了{intMouse}只");
    }
    public void Person()
    {
        ZanTing();
        Console.WriteLine("人醒了");
    }
}
public class Heater
{
    //定义委托
    public delegate void HeaterDelegate();
    //定义事件
    public event HeaterDelegate OnHeater;
    //定义事件的全部
    //public event HeaterDelegate OnHeater1
    //{
    //    add { OnHeater1 += value; }
    //    remove { OnHeater1 -= value; }
    //}

    //触发事件的方法
    public void WakeUp()
    {
        try
        {
            if (OnHeater != null)
            {
                //运行事件
                OnHeater();
            }
            else
            {
                Console.WriteLine("未添加委托");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

匿名函数 

针对于委托,可以快捷的使用委托,不用在写对应的方法

匿名函数分为:普通匿名函数和lambda表达式

普通匿名函数

普通匿名函数并不常用

internal class Program
{
    public delegate void MyDelegate();
    public delegate int MyDelegate1();
    static void Main(string[] args)
    {
        Program program = new Program();
        MyDelegate myDelegate = new MyDelegate(program.Mothod);
        //匿名函数不需要在定义方法可以只写方法名后的
        MyDelegate myDelegate1 = delegate () { };

        Action myDelegate2 = delegate () { };


        MyDelegate1 myDelegate11 = new MyDelegate1(program.Mothod1);

        MyDelegate1 myDelegate12 = delegate() { return 1; };

        Func<int> func = delegate () { return 1; };
    }
    public void Mothod()
    {

    }
    public int Mothod1()
    {
        return 0;
    }
}

lambda表达式(箭头函数)

格式:委托名 对象 = ()=> {  };

()再只有单个参数时可以省略。

=>  箭头

{}只有单行代码时可以省略,多行不能省略

public delegate void MyDelegate();
public delegate int MyDelegate1();
static void Main(string[] args)
{
    MyDelegate myDelegate1 = () => { };
    Action myDelegate2 = () => { };

    MyDelegate1 myDelegate12 = () => { return 1; };
    Func<int> func = () => { return 1; };

}
public void Mothod()
{

}
public int Mothod1()
{
    return 0;
}

运算符重载

作用:对已有的运算符重新定义新的运行规则,以适用不同的数据类型。

关键字:operator

格式:

双目运算符:

public static 数据类型 operator 运算符 (数据类型 对象1,数据类型 对象2) {  代码;   return 对象; }

一目运算符:

public static 数据类型 operator 运算符 (数据类型 对象1) {  代码;      return 对象; }

注意事项:

重载的运算符必须在对应的类中。

运算符在使用时会自动调用重载的运算符。

运算符重载必须是public,必须是静态方法static,绝对不能无返回值void

()的形参跟方法不同,不能使用 ref 和 out

internal class Program
{
    static void Main(string[] args)
    {
        ClassOperator class1 = new ClassOperator();
        class1.A = 1;
        ClassOperator class2 = new ClassOperator();
        class2.A = 2;
        ClassOperator result = class1+class2;
        Console.WriteLine(result.A);
        Console.ReadKey();
    }
}
class ClassOperator
{
    private int a;
    public int A
    {
        get { return a; }
        set { a = value; }
    }
    //运算符重载
    //作用:就是对已有运算符进行重新定义运算规则,以适应不同的数据类型
    //跟方法重载相似

    //关键字:operator

    //注意:
    //必须在本类中,该运算符重载只有在本类中才有用和自动调用
    //自动调用就是使用相关运算符会自动重载

    //必须要是public,且不能没有返回值void(一定要有返回值),必须要是静态方法

    //跟方法不同 ref 和 out 在这里无效

    //不是所有类型的运算符都能重载
    public static ClassOperator operator +(ClassOperator left, ClassOperator right)
    {
        ClassOperator result = new ClassOperator();
        result.a = left.a + right.a;
        return result;
    }
}

异常处理

在C#中,异常处理是编程的一个重要部分,它允许你在程序运行时捕获和处理错误。C#提供了几个关键字和结构来支持异常处理,包括trycatchfinally, 和 throw

关键字:try,catch,finally,throw。

try:检测一个代码块是否有异常。

catch:当触发异常时会运行,该内的代码块

finally:无论是否异常都会执行

throw:暂停程序抛出异常,或定义异常

throw;  仅抛出异常(抛出异常在出现异常处)

try{}catch(Exception ex){   throw ex; (在这里抛出异常)}

throw new Exception(“出现异常”);   自定义异常

格式:

try  
{  
    // 尝试执行的代码  
}  
catch (ExceptionType1 ex)  
{  
    // 处理 ExceptionType1 类型异常的代码  
}  
catch (ExceptionType2 ex)  
{  
    // 处理 ExceptionType2 类型异常的代码  
}  
finally  
{  
    // 无论是否发生异常都会执行的代码  
}
try  
{  
    // 尝试执行的代码  
    if (someCondition)  
    {  
        throw new Exception("自定义异常");  
    }  
}  
catch (Exception ex)  
{  
    // 处理异常的代码  
    throw; // 重新抛出捕获的异常  
}

异常分为:系统异常和自定义异常 

系统异常 

系统本身就有的异常

static void Main(string[] args)
{
    int a = 1;
    int b = 0;
    try
    {
        int c = a / b;
    }
    catch (Exception e)
    {
      //只输出e会获取全部异常信息,添加Message只获取异常信息
        Console.WriteLine(e.Message);
    }
    Console.WriteLine("aaa");
    Console.ReadKey();
}

自定义异常 

通过throw来创建异常

internal class Program
{
        ClassException classException = new ClassException();
        try
        {
            classException.Age = 140;
        }catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        Console.WriteLine("bbb");
        Console.ReadKey();
    }
}
class ClassException
{
    private int _age;
    public int Age
    {
        get { return _age; }
        set
        {
            if (value < 0 || value > 100)
            {
                throw (new Exception("不在年龄范围"));
            }
            else
            {
                _age = value;
            }
        }
    }
}

异常处理原则 

  1. 避免过度捕获:不要捕获你不打算处理的异常类型。这可能会隐藏程序中的潜在问题。
  2. 处理或重新抛出:当捕获到一个异常时,要么处理它(即解决导致异常的问题),要么重新抛出它以便在更高的层次上进行处理。
  3. 记录异常信息:在捕获异常时,记录异常的详细信息(如堆栈跟踪和消息),以便在后续的调查和分析中使用。
  4. 不要使用异常来控制流程:异常应该用于处理异常情况,而不是作为程序控制流程的正常部分。过度使用异常可能导致代码难以理解和维护。
  5. 注意异常的性能开销:虽然异常处理是C#中的一个强大功能,但它也有一定的性能开销。因此,在可以预测并避免异常的情况下,最好直接处理它们而不是依赖异常处理机制。

C#预处理器指令 

预处理器指令指导编辑器在实际编辑开始之前进行预处理。通常用于简化源程序在不同的执行环境中的更改和编辑。

格式:用#号开头,#号之前只能添加空格,预处理指令不是语句所以不需要分号;。

在C#中,并没有像C或C++中的预处理指令(如#include#define#ifdef等)这样的直接等价物。然而,C#提供了几种预处理指令,虽然它们的功能和用途与C/C++的预处理指令有所不同。

以下是C#中的一些“预处理”指令:

  1. #define 和 #undef
    • 在C#中,#define#undef指令并不真正用于定义或取消定义符号,而是作为条件编译的一部分。但它们并不像在C/C++中那样广泛使用,因为C#更推荐使用constenum来定义常量。
    • 在某些情况下,如与编译器指令(如#pragma)结合使用时,可能会看到#define
  2. #pragma
    • #pragma指令用于为编译器提供特定于编译器的指令。例如,可以使用#pragma warning来禁用或修改特定的编译器警告。
  3. #region 和 #endregion
    • 这些指令用于在代码编辑器中折叠或展开代码块,使代码更易于管理。它们并不改变代码的行为,只是为IDE(如Visual Studio)提供指导。
  4. #warning
    • #warning指令允许你在编译时生成自定义的警告消息。这可以用于标记代码中的某些部分,以提醒开发者注意。
  5. #error
    • #error指令用于在编译时生成错误消息。这可以用于在特定条件下阻止代码编译。
  6. #line
    • #line指令允许你更改编译器报告的源代码行号和文件名。这通常用于在代码生成或代码转换工具中,以便在出现问题时提供更准确的错误消息。
  7. 条件编译符号(如#if DEBUG):
    • C#支持基于条件编译符号的条件编译。例如,DEBUG是一个常用的预定义符号,当在Debug模式下编译代码时,它会被设置为true。你可以使用#if DEBUG#elif#else#endif来包含或排除仅在特定条件下需要的代码。

请注意,尽管C#提供了这些“预处理”指令,但它们的用途和语法与C/C++中的预处理指令有所不同。在C#中,更常见的做法是使用constenum和条件编译符号来控制代码的行为。

//预处理器define   de1 定义的符号
#define de1
#define de
//关闭对应的符号
#undef de1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp10
{
    internal class Program
    {
        static string IP="127.0.0.1";
        static void Main(string[] args)
        {
            
            Program program = new Program();
#if de1
            IP = "192.168.40.1";
            Console.WriteLine("if");
            program.Mothod1(IP);
            //!de为false
#elif de
            IP="196.168.5.14";
            Console.WriteLine("elseif");
            program.Mothod(IP);
#else
            Console.WriteLine("else");
 //这个是关闭if标签是必带的
#endif
            
            Console.ReadKey();
        }
        public void Mothod(string IP)
        {
            Console.WriteLine("登录{0}", IP);
        }
        public void Mothod1(string IP)
        {
            Console.WriteLine("注册{0}", IP);
        }
    }
}

多线程 

在C#中,多线程编程的复杂性在于线程的执行顺序是不确定的,除非使用了某种同步机制(如锁、事件、信号量等)来明确指定线程的执行顺序。然而,对于主线程(通常是执行Main方法的线程)和其他由主线程启动的线程之间的关系,有一些常见的行为模式。

当你看到Main方法中的Console.WriteLine()调用似乎在其他线程之前执行时,这可能是由于以下几个原因:

  1. 线程启动顺序:即使你调用了Thread.Start()来启动一个新线程,这并不意味着新线程会立即开始执行。线程调度器负责决定何时启动新线程,以及何时将CPU时间分配给每个线程。因此,主线程可能有机会在新线程开始执行其代码之前继续执行。
  2. 线程优先级:虽然C#线程优先级可以在一定程度上影响线程的调度顺序,但它并不能保证线程执行的精确顺序。即使你设置了高优先级的线程,也不能保证它总是会在低优先级的线程之前执行。
  3. 控制台输出缓冲Console.WriteLine()的输出可能会被缓冲。这意味着,即使Console.WriteLine()方法被调用,实际的输出也可能被延迟,直到缓冲区被刷新或填满。这可能会导致输出顺序看起来与代码中的调用顺序不同。
  4. 代码逻辑:如果Main方法中的Console.WriteLine()调用在启动新线程之前,那么从逻辑上讲,它应该首先执行。但是,这并不意味着新线程中的代码不会立即开始执行,只是Main方法中的代码先执行了而已。
  5. 线程同步:如果你的代码中有同步机制(如锁或等待事件),那么输出顺序可能会受到这些机制的影响。确保没有意外的同步逻辑影响了你对线程执行顺序的观察。

为了更准确地控制多线程程序的输出顺序,你可能需要使用某种形式的同步机制,如MutexSemaphoreMonitorlock关键字、AutoResetEventManualResetEvent等。但是,请注意,过度使用同步机制可能会导致性能下降和死锁等问题。因此,在设计多线程程序时,需要仔细权衡同步需求和性能影响。

static void Main(string[] args)
{
    Program program = new Program();
    //ThreadStart是一个无返回值无参委托
    ThreadStart threadStart = new ThreadStart(program.Mothod);
    //是将threadStart当作参数Thread类的构造函数传入
    Thread thread = new Thread(threadStart);

    //启动新线程,新线程会开始执行Mothod方法
    //这个是
    thread.Start();
    //会先输出Console.WriteLine中的字在thread.Start();前
    //thread.Start();与Thread.Sleep(1000);中间
    Console.WriteLine("start");
    Console.WriteLine("start");
    //暂停一秒主线程
    Thread.Sleep(1000);
    Console.WriteLine("sleep");

    //销毁此线程
    //在运行时,会抛出一个ThreadAbortException异常,在这个异常被捕获时,
    //因为ThreadAbortException异常有Abort标签,会给予代码中一个机会来
    //执行 finally块 和 catch块 中的语句(如果这些块存在并没有来尝试阻止终止)。
    //会直接跳过try块。
    thread.Abort();
    Console.WriteLine("abort");
    Console.ReadKey();
}
public void Mothod()
{
    try
    {
        Console.WriteLine("try");
    }
    catch(ThreadAbortException ex)
    {
        Console.WriteLine($"catch,{ex}");
    }
    finally
    {
        Console.WriteLine("finally");
    }
}

线程池 

线程池的基本工作原理是,在应用程序启动时创建一定数量的线程,并将它们保存在线程池中。当需要执行任务时,从线程池中获取一个空闲的线程,将任务分配给该线程执行。当任务执行完毕后,线程将返回到线程池,可以被其他任务复用。这种设计思想是为了避免频繁地创建和销毁线程的开销,以及控制并发执行的线程数量,从而提高系统的性能和资源利用率。

ThreadPool在创造分线程时,必须传入一个参数,若果传入方法,那么方法的参数类型必须是object。

ThreadPool  不支持线程控制,线程延续,线程取消。

ThreadPool
线程池
必须传一个参数,如果定义方法那形参必须为object类型
                                                          传给形参a
ThreadPool.QueueUserWorkItem(a => { Console.WriteLine(a); }, 1334);
//WaitCallback waitCallback = Mothod1;
//ThreadPool.QueueUserWorkItem(waitCallback,"1");

//ThreadPool.QueueUserWorkItem(a =>
//{
//    int intNum = 1;
//    while (true)
//    {
//        Console.WriteLine("QueueUserWorkItem1");
//        Thread.Sleep(10);
//        intNum++;
//        if (intNum>8)
//        {
//            break;
//        }
//    }
//});
//Thread.Sleep(1000);


//CountdownEvent countdownEvent =new CountdownEvent(9);
//ThreadPool.QueueUserWorkItem(a =>
//{
//    while (true)
//    {
//        Console.WriteLine("QueueUserWorkItem2");
//        Thread.Sleep(10);
//        try
//        { 
//            countdownEvent.Signal();
//        }
//        catch (Exception)
//        {
//            break;
//        }
//    }
//});
//ThreadPool.QueueUserWorkItem(a =>
//{
//    countdownEvent.Wait();
//    int intNum = 1;
//    while (true)
//    {
//        Console.WriteLine("QueueUserWorkItem3");
//        Thread.Sleep(10);
//        intNum++;
//        if (intNum > 2)
//        {
//            break;
//        }
//    }
//});
阻塞运行
//ManualResetEvent manualResetEvent = new ManualResetEvent(false);
//ThreadPool.QueueUserWorkItem(a => { Thread.Sleep(1000); Console.WriteLine(a); manualResetEvent.Set(); }, 123);
//manualResetEvent.WaitOne();
//ThreadPool.QueueUserWorkItem(a =>
//{

//    Console.WriteLine(a);
//}, 456);

Task跟ThreadPool一样都是线程池操作

Task能进行线程控制,线程延续,线程取消。

//Task
// Task能进行线程控制,线程延续,线程取消
//Task是有返回值,有参数至于参数能使用的类型有限
//Wait  等待线程运行完毕
//Task.WaitAll(使用Task的线程);  等待全部线程运行完
//Task.WaitAny(使用Task的线程);   等待任意线程运行完
//ContinueWith   延续某一线程
//CancellationTokenSource  取消线程

//Task<int> task2 = Task.Run(() =>
//{
//    return 1;
//});
//task2.ContinueWith(t =>
//{
//    Console.WriteLine(t.Result);
//});
//Task task = new Task(() =>
//{
//    Console.WriteLine("task");
//});
//task.Start();

//Task.Factory.StartNew(() =>
//{
//    task.Wait();
//    Console.WriteLine("StartNow");
//});
//Thread.Sleep(1000);


CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task task1 = Task.Run(() =>
{
    while (true)
    {
        Console.WriteLine("Task1");
        Thread.Sleep(1000);
        if (token.IsCancellationRequested)
        {
            break;
        }
    }
}, token);

token.Register(() =>
{
    Console.WriteLine("运行完毕后,回调");
});
Thread.Sleep(8000);
cts.Cancel();

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token1 = cancellationTokenSource.Token;
Task task2 = Task.Run(() =>
{
    while (true)
    {
        Console.WriteLine("task2");
        Thread.Sleep(100);
        if (token1.IsCancellationRequested)
        {
            return;
        }
    }
}, token1);
CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
CancellationToken token2 = cancellationTokenSource1.Token;
Task task3 = Task.Run(() =>
{
    Thread.Sleep(1000);
    cancellationTokenSource.Cancel();
    while (true)
    {
        Console.WriteLine("task3");
        Thread.Sleep(1000);
        if (token2.IsCancellationRequested)
        {
            break;
        }
    }
}, token2);
Thread.Sleep(3000);
cancellationTokenSource1.Cancel();

//线程延续
CancellationTokenSource cancellationTokenSource2 = new CancellationTokenSource();
CancellationToken token3 = cancellationTokenSource.Token;
Task task4 = Task.Run(() =>
{
    while (true)
    {
        Console.WriteLine("task2");
        Thread.Sleep(100);
        if (token3.IsCancellationRequested)
        {
            return;
        }
    }
}, token3);
task4.ContinueWith(a =>
{
    CancellationTokenSource cancellationTokenSource3 = new CancellationTokenSource();
    CancellationToken token4 = cancellationTokenSource1.Token;
    Task task5 = Task.Run(() =>
    {
        Thread.Sleep(1000);
        cancellationTokenSource2.Cancel();
        while (true)
        {
            Console.WriteLine("task3");
            Thread.Sleep(1000);
            if (token4.IsCancellationRequested)
            {
                break;
            }
        }
    }, token4);
    Thread.Sleep(3000);
    cancellationTokenSource3.Cancel();

});

异步和同步 

异步  在执行某种操作时,不对线程进行阻塞的操作。

同步  在执行某种操作时,对线程进行阻塞的操作。

异步:

关键字:async     await

使用 async 描述的方法,表示一个异步方法。

只使用 async 描述的方法,不使用await时,在运行时还是同步方法。

async 描述的方法的返回值类型,必须是void 或 Task 或Task<>

await 描述的方法,必须是使用Task线程的方法,且必须有返回值只能是Task或Task<>数据类型

await 是在async 描述的方法中使用时,只影响该方法中 await 之后的代码,至于该方法之外的方法继续运行。也就是说只暂停了该方法之后的方法,主线程继续运行,在await运行完后,在运行该方法之后的代码。

internal class Program
{
    static void Main(string[] args)
    {
        Mothod();
        Console.WriteLine("不想等待下载");
        Console.ReadKey();
    }
    public static async void Mothod()
    {
        Console.WriteLine("下载开始");
        Console.WriteLine("下载中");
        await Mothod1();
        Console.WriteLine("下载完成");
    }
    public static Task Mothod1()
    {
        return Task.Run(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("10%");
            Thread.Sleep(1000);
            Console.WriteLine("30%");
            Thread.Sleep(1000);
            Console.WriteLine("60%");
            Thread.Sleep(1000);
            Console.WriteLine("90%");
            Thread.Sleep(1000);
            Console.WriteLine("100%");
        });
    }
}

File创建文件

在C盘或D盘中创建文本文件用来存储数据

File类

File类提供了对文件的 创建,读取,写入,复制,移动,删除,重命名等操作和方法。

只有在创建文件时,需要用FileStream创建对象来接受返回值,用于关闭FileStram防止使用到相关的进程和线程无法访问。

文件地址     C:\文件夹名

文件夹名是不存在的文件夹的名,也就是你要创建的文件夹的名

Directory创建文件夹:

 Directory.CreateDirectory(文件地址);

File创建文件:

仅对文件有用

文件地址     C:\myfile.txt

string path =@"C:\myfile.txt";//文件为不存在文件,myfile.txt为自定义文件名

FileStream fileStream = Fille.Create(path);

fileStream.Close();//只有创建文件时才需要关闭

path就是文件地址。

写入文件:

File.WriteAllText(path,字符串);

字符串就是要保存的数据

File.WriteAllLines(path,字符串数组);

读取文件:

string str = File.ReadAllText(path);这个读取后是一个字符串

string[] strArray = File.ReadAllLines(path);这个写入时数组读取后也是一个数组

删除文件:

File.Delete(path);

会直接删除文件

移动文件:

File.Move(path,新文件);

新文件为已存在文件

复制文件:

File.Copy(path,新文件);//深克隆不影响原文件

新文件为已存在文件

StreamWriter创建文件:

要随用随关,要使用StreamWriter需要在未关闭时才能输入

可以同时添加多个数据不换行

StreamWriter writeText = new StreamWriter(文件地址);//文件为不存在文件,要自定义文件名

writeText.Write(数据);

writeText.WriteLine(数据);

writeText.Close();

StreamReader读取文件:

要随用随关,要使用StreamReader需要在未关闭时才能输入

StreamReader readText = new StreamReader(文件地址 , Encoding.UTF8);//文件为不存在文件,要自定义文件名。Encoding.UTF8这是读取文件中数据的格式

readText.ReadToEnd(文件地址);

readText.Close();

追加文件

无论是File创建文件还是StreamWriter创建文件 及各种操作都会覆盖原有文件。

未有文件时:

要注意:FileMode.Create 和 FileMode.Append 前一个是创建文件,后一个是追加文件。

FileStream fileStream = new FileStream(文件地址 , FileMode.Create , FileAccess.Write);

StreamWriter addText = new StreamWriter(fileStream);

addText.Write();//可以同时添加多个数据不换行

addText.WriteLine();

addText.Close();

已有文件时:

FileStream fileStream = new FileStream(文件地址 , FileMode.Append , FileAccess.Write);

StreamWriter addText = new StreamWriter(fileStream);

addText.Write();//可以同时添加多个数据不换行

addText.WriteLine();

addText.Close();

fileStream.Close();

读取文件用.File中的ReadAllText 或 StreamReader中的ReadToEnd 都行

 internal class Program
 {
     static void Main(string[] args)
     {
         //File类创建文件
         const string CONST_PATH = @"C:\Users\MINME\Desktop\myFile.txt";
         //这种直接创建会造成绑定并无法关闭线程导致其他进程或线程无法访问
         //File.Create(CONST_PATH);
         //File类中Create方法会返回一个FileStream类型的参数
         //FileStream file = File.Create(CONST_PATH);
         //file.Close();
         


         FileClass.FileMothod(CONST_PATH);
         FileClass.WriteAllTestMothod(CONST_PATH, "aaa");
         FileClass.ReadAllTestMothod(CONST_PATH);
         string[] strArray = { "sddd", "678", "334" };
         FileClass.WriteAllLinesMothod(CONST_PATH, strArray);
         FileClass.ReadAllLinesMothod(CONST_PATH);


         FileClass.StreamWriterMothod(CONST_PATH, "ccccc");
         FileClass.streamReaderMothod(CONST_PATH);

         FileClass.AppendFileStream(CONST_PATH, "append");
         FileClass.AppendStreamReader(CONST_PATH);
         Console.ReadKey();
     }
 }
 class FileClass
 {
     //创建文件的方法
     public static bool FileMothod(string path)
     {
         try
         {
             if (!File.Exists(path))
             {
                 //创建文件
                 FileStream fileStream = File.Create(path);
                 //关闭线程
                 fileStream.Close();
                 Console.WriteLine("创建文件成功");
                 return true;
             }
             else
             {
                 Console.WriteLine("文件已存在");
             }

         }
         catch (Exception e)
         {
             Console.WriteLine($"创建文件问题:{e.Message}");
         }
         
         return false;
     }
     //添加字符串
     //覆盖原有文件
     public static void WriteAllTestMothod(string path,string str)
     {
         File.WriteAllText(path, str);
         Console.WriteLine("添加字符串成功");
     }
     //读取字符串
     public static void ReadAllTestMothod(string path)
     {
         string str = File.ReadAllText(path);
         Console.WriteLine(str);
     }
     //添加字符串数组
     //覆盖原有文件
     public static void WriteAllLinesMothod(string path,string[] strArray)
     {
         File.WriteAllLines(path, strArray);
         Console.WriteLine("添加字符串数组成功");
     }
     //读取字符串数组
     public static void ReadAllLinesMothod(string path)
     {
         string[] strArray = File.ReadAllLines(path);
         foreach (string str in strArray)
         {
             Console.WriteLine(str);
         }
     }
     //删除文件
     public static void DeleteMothod(string path)
     {
         File.Delete(path);
         Console.WriteLine("删除文件成功");
     }   
     //将指定文件移到新位置
     public static void MoveMothod(string path,string path1)
     {
         File.Move(path,path1);
         Console.WriteLine($"移动文件到{path1}");
     }
     //深复制Copy
     public static void CopyMothod(string path1,string path2)
     {
         File.Copy(path1,path2);
         Console.WriteLine($"将{path1}文件复制到{path2}");
     }


     //streamWriter和streamReader
     //跟File一样都会覆盖原有的文件
     //添加
     public static void StreamWriterMothod(string path,string str)
     {
         StreamWriter streamWriter = new StreamWriter(path);
         streamWriter.WriteLine(str);
         streamWriter.Close();
     }
     //读取
     public static void streamReaderMothod(string path)
     {
         //                                                字符编码格式在读时用到,仅用于StreamReader
         StreamReader streamReader = new StreamReader(path, Encoding.UTF8);
         streamReader.ReadToEnd();
         streamReader.Close();
     }

     //追加
     public static void AppendFileStream(string path,string str)
     {
         if (!File.Exists(path))
         {
             FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
             StreamWriter streamWriter = new StreamWriter(fileStream);
             streamWriter.WriteLine(str);
             streamWriter.Close();
             fileStream.Close();
         }
         else
         {
             FileStream fileStream = new FileStream(path,FileMode.Append, FileAccess.Write);
             StreamWriter streamWriter= new StreamWriter(fileStream);
             streamWriter.WriteLine(str);
             streamWriter.Close();
             fileStream.Close();
         }
     }
     //读取
     public static void AppendStreamReader(string path)
     {
         StreamReader reader = new StreamReader(path,Encoding.UTF8);
         string str = reader.ReadToEnd();
         Console.WriteLine(str);
     }
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值