笔者已转岗,转为安卓/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类下的方法,常用的方法有
- 筛选:
Where
c
var adults = people.Where(p => p.Age >= 18); - 投影:
Select
var names = people.Select(p => p.Name); - 排序:
OrderBy
、ThenBy
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 }); - 聚合:
Sum
,Average
,Count
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;
}
}
}