C#语法学习记录
本文最后更新于58 天前,其中的信息可能已经过时

笔者已转岗,转为安卓/C#开发
由于笔者此前C#接触的不多,因此需要从头开始学习语法
本文着重记录笔者认为C#中需要注意的语法或其他地方
笔记会有点乱

1、对于频繁变化的String类型,因使用StringBuilder类代替String类
构造方法

public StringBuilder(string value);
public StringBuilder(string value, int cap);

方法

sb.Append(); //添加字符串到末尾
sb.AppendFormat(); //添加自定义变量的格式的值到末尾
sb.Insert(); //插入指定位置
sb.Remove(); //从指定位置移除指定数量的字符串
sb.Replace(); //将指定的字符串替换成指定的字符串

2、需要在C#中使用正则表达式

方法

System.Text.RegularExpressions.Regex.IsMatch(String str , flow); //正则表达式flow在str中石油找到匹配项
Match(); //在字符串中搜索正则表达式的匹配项,并将精确结果作为单个Match对象返回
Matches(); //同Match(),但返回所有成功的匹配
Replace(); //用指定的替换字符串替换由正则表达式定义的所有匹配项
Split(); //在由正则表达式匹配的位置将输入字符串拆分为一个子字符串数组

3、goto:直接跳转到指定标签语句内(必须要有标签语句)

goto Fun1;

Fun1:
    Console.ReadLine();

4、数组相关操作

int[] arr1;
int[,] arr2;

arr1.Length
arr2.GetLength(0)
arr2.GetLength(1)

foreach(类型 变量名 in arr)
{
    语句块
} //遍历数组,注意,foreach中的变量是只读的,这个方法不能对原数组元素进行操作

数组在声明时必须声明长度,若对数组元素进行添加/修改,必须新建一个数组然后复制过去
在处理动态长度的集合时,通常使用List<T>

List<T>常用方法

List<int> list = new List<int>();
list.Add(1);

List<int> moreItems = new List<int> { 2, 3 };
list.AddRange(moreItems);

list.Insert(1, 4);

List<int> anotherList = new List<int> { 5, 6 };
list.InsertRange(2, anotherList);

list.Remove(1); //移除值为1的元素

list.RemoveAt(0); //移除索引为0的元素

list.RemoveAll(x => x % 2 == 0); // 移除所有符合条件的x的元素

list.Clear(); // 清空所有元素

bool contains = list.Contains(3); //查找是否存在指定元素

int index = list.IndexOf(3); //查找指定索引元素,未找到返回-1

int lastIndex = list.LastIndexOf(3); //查找指定元素的最后一个匹配项的索引

int firstEven = list.Find(x => x % 2 == 0); //查找符合条件的x的第一个元素

List<int> evens = list.FindAll(x => x % 2 == 0); //

bool hasEven = list.Exists(x => x % 2 == 0); //查找是否存在符合调价内地元素

list.Sort();

list.Reverse();

int count = list.Count;

int capacity = list.Capacity; //容量
list.Capacity = 100;

int[] array = list.ToArray();

5、哈希表用于大数据查询和缓存机制等业务场景中,分为非泛型Hashtable和泛型Dictionary<TKey, TValue>,字典类型性能好,安全系数高,但是在处理多线程时(哈希冲突)需要选择ConcurrentDictionary或手动配置

// Hashtable
Hashtable hashtable = new Hashtable();
hashtable.Add("Key1", 100);  // 添加键值对
hashtable["Key2"] = 200;     // 索引器赋值(若键存在则覆盖)

// Dictionary
Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("Key1", 100);
dict["Key2"] = 200;

hashtable.Remove("Key1");  // 删除键
dict.Remove("Key1");

// 检查键是否存在
bool exists = hashtable.ContainsKey("Key1");
bool existsDict = dict.ContainsKey("Key1");

// 获取值
int value;
if (dict.TryGetValue("Key1", out value)) {
    // 成功获取
}

// Hashtable 遍历 DictionaryEntry
foreach (DictionaryEntry entry in hashtable) {
    Console.WriteLine($"{entry.Key}: {entry.Value}");
}

// Dictionary 遍历 KeyValuePair
foreach (KeyValuePair<string, int> pair in dict) {
    Console.WriteLine($"{pair.Key}: {pair.Value}");
}

6、析构函数

.NET Framework类库有垃圾回收功能,当某个类的实例被认为不再有效且符合析构条件时,垃圾回收功能会自动调用析构函数

析构函数的命名为 ~类名

7、属性的get/set访问器

使用类似java中的get/set

class Object{
 private int num;
 public int Num{
  set{
   num = value;
   //其他操作
  }
  get{
   return num;
  }
 }
}
//或者自动实现,可以写为
public int Num{
 set;
 get;
}
//调用时可直接使用
Object obj = new Object();
obj.Num = 1;

8、类的继承方法如下:

class Obj1{}
class Obj2: Obj1{}

//调用父类方法用base.fun();

9、abstract方法和virtual方法的区别

abstract方法必须在抽象类中声明,不允许有方法体,子类必须用override重写

virtual方法可以不在抽象类中声明,可以有方法体,子类不必须重写

10、结构类型(struct)

结构可以看作值类型的类(class),内存分配和释放上相比类更高效,但不允许继承,只能实现接口;

public struct area{
 public double width;
 public double height;
 public double Area(){
  return width * height;
 }
}

11、out参数

out参数可以将方法中的形参指向的内存暴露给调用者

12、参数数组

在声明中的形参前加上param标识符,表示形参需传入一系列指定类型的实参

void fun(param int[] num){}

fun(num1,num2,num3…);

13、ref局部变量

ref可以将变量创建一个别名,如ref int x =ref y中,xy指向同一个内存空间

14、索引器

索引器可以优化属性访问方法,也可以通过规范的方式访问敏感字段

定义如下

class Project{
 private string str1;
 private string str2;
 private string str3;
 public string this[int var]{
  set{
   switch(var){
    case 0:str1 = value;
     break;
    case 1:str2 = value;
     break;
    case 2:str3 = value;
     break;
    default:
      throw new ArgumentOutOfRangeException("var");
   }
  }
  get{...}
}

public static void Main(){
 Project p1 = new Project();
 p1[0] = "str";
 ...
}

15、屏蔽基类成员

class Pjt1{
 public string str1 = "str";
}
class Pjt2: Pjt1{
 new string str1 = "str1";
}

16、密封类(sealed)

和抽象类相反,不能用作基类

17、运算符重载(operator):重新定义运算符规则

18、using语句

程序可能会因一些异常,无法进入资源释放的代码;使用using语句可以自动调用Dispose方法
程序的结构从
try{…}finally{…; class.Dispose;}变为using(…){…},简化了代码,避免了资源释放的问题

19、枚举类型的位标志

在声明枚举类型时,添加[Flags]标识符,可以对枚举值进行按位处理和组合处理,使用HashFlag方法简化多标签存在的逻辑

[Flags]
enum Options
{
    None = 0,
    A = 1,  // 二进制 0001
    B = 2,  // 二进制 0010
    C = 4   // 二进制 0100
}

class Program
{
    static void Main()
    {
        // 组合多个选项
        Options ops = Options.A | Options.C;

        // 检查选项
        Console.WriteLine(ops.HasFlag(Options.A)); // True
        Console.WriteLine(ops.HasFlag(Options.B)); // False
        Console.WriteLine(ops.HasFlag(Options.C)); // True
    }
}

//输出
True
False
True

enum Options
{
    None = 0,
    A = 1,  // 二进制 0001
    B = 2,  // 二进制 0010
    C = 4   // 二进制 0100
}

class Program
{
    static void Main()
    {
        // 尝试组合多个选项
        Options ops = Options.A | Options.C;

        // 检查选项
        Console.WriteLine(ops.HasFlag(Options.A)); // False
        Console.WriteLine(ops.HasFlag(Options.B)); // False
        Console.WriteLine(ops.HasFlag(Options.C)); // False
    }
}

输出
False
False
False

20、委托(delegate)

delegate可以看作一个包含有序方法列表的对象

委托的基本语法

delegate int Mydel(int a,int b);
public int Add(int a,int b){
 return a+b;
}
public int Multiply(int a, int b){
 return a*b;
}
public static void Main(){
 Mydel del;
 int a = 1, b = 2;
 del = Add;
 Console.WriteLine(del(a,b));
 del = Multiply;
 Console.WriteLine(del(a,b));

 //或使用Invoke判断委托是否为空
 del?.Invoke(a,b);

 Mydel del1,del2,del3;
 del1 = Add;
 del2 = Multiply;
 del3 = del1 + del2; // 组合委托
 del1 += Multiply; //添加方法
 del1 -= Multiply; //移除方法

 Mydel del4 = delegate(int a)
             {
                 return a+20;
             };//匿名方法

 Mydel del5 = (int a) => { return a+20;}; //Lambda 表达式简化匿名方法
 del5 = (a) => {return a+20;};
 del5 = a => {return a+20;};
 del5 = a => a+20;
}

21、事件(event)

事件类似于JavaScript中的事件监听器

delegate void Handler();

class Incrementer
{
    public event Handler CountedADozen;
    
    public void DoCount()
    {
        for (int i = 1; i < 100; i++)
        {
            if (i % 12 == 0 && CountedADozen != null)
            {
                CountedADozen?.Invoke();
            }
        }
    }
}

class Dozens
{
    public int DozensCount { get; private set; }
    
    public Dozens(Incrementer incrementer)
    {
        DozensCount = 0;
        incrementer.CountedADozen += IncrementDozensCount;
    }
    
    void IncrementDozensCount()
    {
        DozensCount++;
    }
}

class Program
{
    static void Main()
    {
        Incrementer incrementer = new Incrementer();
        Dozens dozensCounter = new Dozens(incrementer);
        incrementer.DoCount();
        Console.WriteLine("Number of dozens = {0}", dozensCounter.DozensCount);
    }
}

这是一个简单的事件调用示例,主程序通过调用发布者的方法,间接触发事件,发布者主动控制事件的触发时机;订阅者注册了发布者的事件,事件触发时,调用订阅者的方法。

在实际使用过程中,通常使用标准的EventHandler委托类型:public delegate void EventHandler(object sender, EventArgs e);

将上述示例进行修改

class Incrementer
{
    public event EventHandler CountedADozen;
    
    public void DoCount()
    {
        for (int i = 1; i < 100; i++)
        {
            if (i % 12 == 0 && CountedADozen != null)
            {
                CountedADozen?.Invoke(this, null);
            }
        }
    }
}

class Dozens
{
    public int DozensCount { get; private set; }
    
    public Dozens(Incrementer incrementer)
    {
        DozensCount = 0;
        incrementer.CountedADozen += IncrementDozensCount;
    }
    
    void IncrementDozensCount(object source, EventArgs e)
    {
        DozensCount++;
    }
}

class Program
{
    static void Main()
    {
        Incrementer incrementer = new Incrementer();
        Dozens dozensCounter = new Dozens(incrementer);
        incrementer.DoCount();
        Console.WriteLine("Number of dozens = {0}", dozensCounter.DozensCount);
    }
}

如果需要在使用标准委托传递参数,可以派生EventArgs,并传入派生类的对象

22、接口(interface)

对对象数组使用如Sort等方法时,需要让类继承IComparable接口,并实现ConpareTo(object obj)接口

接口是应用类型,对于继承了接口的类的对象,可以使用强制类型转换将对象转换成接口的引用。

23、拓展方法

可以拓展类的方法(必须声明为static、必须是静态类的成员、比一个参数类型必须有关键字this)

24、协变

协变必须在类型参数中指定out关键字

25、匿名类型

var c = new { c1=”属性一”, c2 = 1};
匿名对象只可读

26、LINQ

LINQ分为查询表达式和查询语法
查询表达式类似SQL语句,格式为from … where … select … (group … by …) (into… )
查询语法是System.LINQ.Enumerable类下的方法,常用的方法有

  • 筛选Wherec
    var adults = people.Where(p => p.Age >= 18);
  • 投影Select
    var names = people.Select(p => p.Name);
  • 排序OrderByThenBy
    var sorted = people.OrderBy(p => p.LastName).ThenBy(p => p.FirstName);
  • 分组GroupBy
    var groups = from p in people group p by p.Department into g select new { Department = g.Key, Employees = g };
  • 连接Join
    var query = customers.Join(orders, c => c.Id, o => o.CustomerId, (c, o) => new { c.Name, o.Amount });
  • 聚合SumAverageCount
    var total = products.Sum(p => p.Price);

将委托作为LINQ参数
C#内置了一个委托类型Func<输入类型, 返回值类型>
LINQ的查询方法可以使用委托作为输入参数,委托可以用委托对象,Lamdba表达式,匿名方法

LINQ to XML:
使用LINQ查询XML有两种方式:简化的XML操作API、使用LINQ查询工具
API:常用的XML类有XElement、XAttribute、XDocument
常用的获取XML值的方法有 Nodes、Elements、Element、Descendants、DescendantsAnndSelf、Ancestors、AncestorsAndSelf、Parent

27、异步(asnyc、await)

异步方法的方法头必须包含async关键字,返回类型必须是Task、Taks<T>、ValueTask<T>、void或任何具有可访问的GetAwaiter方法类型中的任一种

任何返回Task<T>类型的异步方法,返回值必须为T类型或可以隐式转换为T的类型

Task.Run需要一个Func<TReturn>委托作为参数,如果要将自定义的方法传给Task.Run方法,需要基于该方法创建一个委托

如果要取消一个异步操作,需要创建CancellationToken和CancellationTokenSource的对象

Task.Yield可以将异步任务中断,将处理器释放给其他任务

示例代码
namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        CancellationTokenSource cancellationTokenSource;
        CancellationToken cancellationToken;
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void btnProcess_Click(object sender, RoutedEventArgs e)
        {
            btnProcess.IsEnabled = false;
            btnCancel.IsEnabled = true;

            cancellationTokenSource = new CancellationTokenSource();
            cancellationToken = cancellationTokenSource.Token;

            int completedPercent = 0;
            for(int i = 0; i < 10; i++)
            {
                if(cancellationToken.IsCancellationRequested)
                    break;
                try
                {
                    await Task.Delay(1000, cancellationToken);
                    completedPercent = (i + 1) * 10;
                }
                catch (TaskCanceledException ex)
                {
                    completedPercent = i * 10;
                }
                processBar.Value = completedPercent;
            }

            string message = cancellationToken.IsCancellationRequested
                ? string.Format($"Process was cancelled at {completedPercent}%.")
                : "Process completed normally.";
            MessageBox.Show(message,"Completion Status");
            processBar.Value = 0;
            btnProcess.IsEnabled = true;
            btnCancel.IsEnabled = false;
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            if (!btnProcess.IsEnabled)
            {
                btnCancel.IsEnabled = false;
                cancellationTokenSource.Cancel();
            }
        }
    }
}

28、BackGroundWorker类(此方法已逐渐被取代)

后台线程,可以与主线程通信

示例代码
namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        BackgroundWorker bgWorker = new BackgroundWorker();
        public MainWindow()
        {
            InitializeComponent();

            bgWorker.WorkerReportsProgress = true;
            bgWorker.WorkerSupportsCancellation = true;

            bgWorker.DoWork += DoWork_Handler;
            bgWorker.ProgressChanged += ProgressChanged_Handler;
            bgWorker.RunWorkerCompleted += RunWorkerCompleted_Handler;
        }

        private void RunWorkerCompleted_Handler(object sender, RunWorkerCompletedEventArgs e)
        {
            processBar.Value = 0;

            if(e.Cancelled)
                MessageBox.Show("取消");
            else
                MessageBox.Show("完成");
        }

        private void ProgressChanged_Handler(object sender, ProgressChangedEventArgs e)
        {
            processBar.Value = e.ProgressPercentage;
        }

        private void DoWork_Handler(object sender, DoWorkEventArgs e)
        {
            if (sender == null) return;
            BackgroundWorker worker = sender as BackgroundWorker;
            for (int i = 0; i < 10; i++)
            {
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    worker.ReportProgress(i * 10);
                    Thread.Sleep(500);
                }
            }
        }

        private void btnProcess_Click(object sender, RoutedEventArgs e)
        {
            btnProcess.IsEnabled = false;
            btnCancel.IsEnabled = true;
            if(!bgWorker.IsBusy)
                bgWorker.RunWorkerAsync();
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            bgWorker.CancelAsync();
            btnCancel.IsEnabled = false;
            btnProcess.IsEnabled = true;
        }
    }
}
文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇