只会记录与其他语言稍有不同或者说特有的部分,因为一门编程语言,相同的功能可能表现形式不同,可能为了更加简便提供类似语法糖的特性。

特性

简介
  1. 特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、枚举、属性等)的行为信息的声明性标签
  2. 规定特性的语法如下:
// 特性位于应用的元素之前,应用的元素比如说类,方法
[attribute(positional_parameters, name_parameter = value, ...)]
预定义特性
  1. [Obsolete]:标记为过时的特性,提示该方法或类已被弃用
  2. [Serializable]:使类可以被序列化
  3. [NonSerialized]:与 [Serializable] 一起使用,用于指定不希望序列化的字段
  4. [DllImport]
  5. [Conditional]:用于指定在满足特定条件下才编译的方法(条件编译)
  6. [AttributeUsage]:用于定义自定义特性的使用范围和限制,这个预定义特性用于自定义特性。
    1. 规定该特性的语法为:
    [AttributeUsage(
       validon,     // 这个参数规定自定义的特性可以应用的元素,比如说类,方法
       AllowMultiple=allowmultiple, // 该特性是否可以多次使用
       Inherited=inherited          // 该特性是否可以被派生类继承
    )]
    
    
    1. 示例:
    namespace test_c
    {
        [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
        class AnatationAttribute : Attribute
        {
            public AnatationAttribute(string description)
            {   
                this.description = description;
            }
            protected string description;
            public string Description
            {
                get { return description; }
            }
        }
    
        
        [Anatation("这是程序入口的主类")]
        [Anatation("这是程序入口的主类")]
    
        class Application
        {
            [Anatation("入口方法为Main")]
            static void Main(string[] args)
            {
            }
        }
    }
    
  7. [Flags]:用于定义枚举类型,使其可以被当作位域处理
  8. [DefaultValue]:指定字段的默认值
  9. [Description]:常用于添加属性或方法的描述信息
自定义特性

自定义特性的使用包含以下步骤:

  1. 自定义特性的定义:一个自定义的特性类应继承自System.Attribute 类
  2. 在目标程序元素上应用自定义特性:将自定义特性置于需要应用的元素之上
  3. 通过反射访问特性

反射

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

  1. 示例:使用反射获取类的自定义特性
using System;
using System.Reflection;

public class SampleClass
{
    public string Name { get; set; }

    [Obsolete("This method is obsolete.")]
    public void OldMethod()
    {
        Console.WriteLine("This is an old method.");
    }

    public void NewMethod()
    {
        Console.WriteLine("This is a new method.");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        // 获取SampleClass类型的信息
        Type type = typeof(SampleClass);

        // 输出类名
        Console.WriteLine("Class Name: " + type.Name);

        // 获取并输出属性信息
        PropertyInfo[] properties = type.GetProperties();
        foreach (var prop in properties)
        {
            Console.WriteLine("Property Name: " + prop.Name + ", Property Type: " + prop.PropertyType);
        }

        // 获取并输出方法信息
        MethodInfo[] methods = type.GetMethods();
        foreach (var method in methods)
        {
            Console.WriteLine("Method Name: " + method.Name);

            // 检查方法是否有特性
            foreach (var attr in method.GetCustomAttributes())
            {
                Console.WriteLine(" - Attribute: " + attr.GetType().Name);
            }
        }
    }
}


// 输出如下
Class Name: SampleClass
Property Name: Name, Property Type: System.String
Method Name: get_Name
 - Attribute: CompilerGeneratedAttribute
Method Name: set_Name
 - Attribute: CompilerGeneratedAttribute
Method Name: OldMethod
 - Attribute: ObsoleteAttribute
Method Name: NewMethod
Method Name: Equals
 - Attribute: __DynamicallyInvokableAttribute
Method Name: GetHashCode
 - Attribute: __DynamicallyInvokableAttribute
Method Name: GetType
 - Attribute: SecuritySafeCriticalAttribute
 - Attribute: __DynamicallyInvokableAttribute
Method Name: ToString
 - Attribute: __DynamicallyInvokableAttribute

属性

属性是类和结构体中用于封装数据的成员

set,get访问器

一种简化写法,等同于语法糖

  1. 只读属性:需要属性只读,则不写set访问器
  2. 只写属性:需要属性只写,则不写get访问器
class Test
{
    private string name;
    private int age;
    
    // 只读属性name
    public string Name
    {
        get { return name; }
    }
    // 只写属性age
    public int Age
    {
        set { age = value; }
    }

    public int getAge()
    {
        return age;
    }
    public Test(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
}
class Application
{
    static void Main(string[] args)
    {
        Test test = new Test("Nrvcer", 24);
        Console.WriteLine("Name: " + test.Name);
        
        test.Age = 25;
        Console.WriteLine("Age: " + test.getAge());

    }
}
抽象属性
  1. 抽象类可拥有抽象属性,这些属性应在派生类中被实现
  2. 示例如下:
 abstract class Person
 {
     public abstract string Name { get; set; }
     public abstract int Age { get; set; }
 }

 class Student : Person
 {
     public override string Name { get; set; }
     public override int Age { get; set; }
     public override string ToString()
     {
         return "Name: " + Name + ", Age: " + Age;
     }
 }
 class Application
 {
     static void Main(string[] args)
     {
         Student student = new Student { Name = "Nrvcer", Age = 24 };
         Console.WriteLine(student.ToString());

     }
 }

索引器

  1. 索引器允许一个对象可以像数组一样使用下标的方式来访问。和get,set访问器不同的是前者返回或者设置一个特定的成员变量,而索引器返回或者设置对象实例的一个特定值
  2. 一维索引器的定义语法如下:
element-type this[int index] 
{
   // get 访问器
   get 
   {
      // 返回 index 指定的值
   }

   // set 访问器
   set 
   {
      // 设置 index 指定的值 
   }
}
  1. 索引器示例如下:
 class Test
 {
     private string[] arr = new string[number];
     public static int number = 10;

     public Test()
     {
         for (int i = 0; i < number; i++) {
             arr[i] = "Hello World!";
         }
     }

     public string this[int index]
     {
         get {
             if (index < 0 || index >= number) {
                 throw new IndexOutOfRangeException();
             }
             return arr[index];
         }
         set {
             if (index < 0 || index >= number) {
                 throw new IndexOutOfRangeException();
             }
             arr[index] = value;
         }
     }
 }
 class Application
 {
     static void Main(string[] args)
     {
         Test t = new Test();
         t[6] = "test";
         for (int i = 0; i < Test.number; i++)
         {
             Console.WriteLine(t[i]);
         }

     }
 }
  1. 索引器的重载:索引器的参数可以是其他类型的

集合

C#对各种数据结构,如栈,队列,列表,哈希表等都已实现,放置在System.Collections命名空间下。详情略。

泛型

  1. 泛型接口
  2. 泛型类
  3. 泛型方法
  4. 泛型事件
  5. 泛型委托:示例如下
using System;

namespace test_c  {
    public delegate void PrintDelegate<T>(T t);
    class Application {
        public void print_string(string str)
        {
            Console.WriteLine(str);
        }
        public void print_int(int num)
        {
            Console.WriteLine(num);
        }
        static void Main(string[] args) {
            PrintDelegate<string> print_string_delegate = new PrintDelegate<string>(new Application().print_string);
            PrintDelegate<int> print_int_delegate = new PrintDelegate<int>(new Application().print_int);
            print_string_delegate("Hello World");   
            print_int_delegate(100);
            Console.ReadKey();
        }
    }
}

委托

基础

在 C# 中,委托(Delegate)是一种类型安全的函数指针,可以指向任何与其签名匹配的方法,它允许将方法作为参数传递给其他方法。

  1. 委托的声明:一般声明在外面,与类是平级的
public delegate <return type> <delegate-name> <parameter list>
  1. 委托变量的创建:<delegate-name> 变量名
  2. 委托方法定义:委托方法的原型需要和委托一致
  3. 委托变量绑定委托方法进行实例化:使用new delegate-name(被委托的方法名)语法进行委托的实例化
    1. 委托的单播:一个委托变量只需要绑定一个方法
    class Test
    {
        public delegate  int add(int a, int b);
    }
    class Application
    {
        static void Main(string[] args)
        {
            Test t = new Test();
            Test.add ad = new Test.add(add);
    
            Console.WriteLine(ad(2, 3));
        }
        static int add(int a, int b)
        {
            return a + b;       
        }
    }
    
    1. 委托的多播:使用+运算符将相同类型的委托合并到一个委托中,这个委托调用时,相同类型的委托关联的方法被调用。如果你不再需要某个方法,可以通过-=运算符将该方法从委托链中移除
  4. 委托的调用
内置委托
  1. Action:代表不返回值的方法
  2. Func:代表有返回值的方法
  3. Predicate:代表返回 bool 值的方法
应用场景
  1. 基于委托实现主窗体向子窗体传值
  2. 基于委托实现子窗体向主窗体传值

事件

简介
  1. 事件本质上就是一个委托类型,其使用发布-订阅模型,利用委托来存储和调用多个订阅方法
  2. 事件是一个存储委托方法的私有字段,编译自会自动生成add和remove方法来管理委托列表
事件的使用
  1. 委托的声明:定义事件将使用的委托类型
  2. 事件的声明:通过event关键字声明一个事件
  3. 事件的触发:在合适的时机调用事件,通知所有订阅者(调用委托的方法列表)
  4. 事件的订阅和取消订阅:通过+=-=运算符订阅和取消订阅事件
  5. 示例如下:
// 泛型委托
public delegate void ClickEventHandler<T>(object sender, T args);

class PushButton<T>
{
    // 定义泛型事件
    public event ClickEventHandler<T> clicked;

    public void TriggerEvent(T args)
    {
        clicked ?. Invoke(this, args);  // 触发事件
    }
}
class Application
{
    static void Main(string[] args)
    {
        PushButton<string>  button = new PushButton<string>();
        button.clicked += (sender, message) => Console.WriteLine("Button clicked with args: " + message);
        
        // 用户点击按钮
        button.TriggerEvent("user click button");

        Console.ReadKey();
    }
}
事件机制实现线程之间的通信。

不安全代码

托管代码与非托管代码
  1. 托管代码:由.NET语言运行环境(CLR,Common Language Runtime)管理的代码。CLR 提供了内存管理、类型安全性、异常处理等服务,开发者无需直接操心底层资源的分配和释放。
  2. 非托管代码:非托管代码是直接运行在操作系统上的代码,不依赖 CLR 提供的服务。开发者需要手动管理内存、资源分配和释放,容易出现资源泄漏或访问非法内存的问题。例如C#程序中使用 [DllImport("xxx.dll")]特性调用C++语言编写的DLL函数时,就使用了非托管代码。
不安全代码
  1. 定义:指允许直接使用指针(pointer)和操作内存的代码块。这样的代码块使用unsalf修饰符进行标记
  2. 实例:在C#中使用不安全代码实现两个整型值的交换
class Program
{
    public unsafe static void swap(int* p, int* q)
    {
        int temp = *p;
        *p = *q;
        *q = temp;
    } 
    static void Main(string[] args)
    {
        unsafe
        {
            int a = 10, b = 20;
            Console.WriteLine("a = {0}, b = {1}", a, b);
            swap(&a, &b);
            Console.WriteLine("a = {0}, b = {1}", a, b);
        }
    }
}
  1. 在不安全代码中,如果你需要操作托管类型(如数组、字符串等),它们的内存位置可能会被垃圾回收器移动,因此需要使用 fixed 关键字固定其内存地址。
public unsafe static void Main()
{
    int[] list = { 10, 100, 200 };
    fixed (int* ptr = list)

    for (int i = 0; i < list.Length; i++)
    {
        Console.WriteLine("Address of list[{0}]={1}", i, (int)(ptr + i));
        Console.WriteLine("Value of list[{0}]={1}", i, *(ptr + i));
    }
    Console.ReadKey();
}

多线程

关键字

  1. partial:用于将一个类、结构体、接口或方法的定义拆分到多个文件中。它允许你将一个类型的实现分散在多个文件中。示例如下:
// 一个文件中
public partial class SerialBus
{
    public string portName
    {
        get { return portName; }
        set { portName = value; }
    }
}
// 另一个文件中
public partial class SerialBus
{
    public string info() {
        return "port:COM1";
    }
}

// OK
SerialBus serialBus = new SerialBus();
Console.WriteLine(serialBus.info());