C# 一些學習小筆記及技巧

學習及工作中積累了一些零零碎碎的小筆記及技巧(C#),重溫和匯總一下。

1. 類別轉換時,如果你未能確定數值有效,使用TryParse 比 Parse 要更安全, TryParse 不會引發異常。
2. ?同??
1) 數據類型? 表示該類型變量可被賦為Null
例如: int 類型的默認值為0,int? 默認為 null
int i; //默認為0
int? i ; //默認為null
2) ?? 如果變量為null 時,默認的值
例如:
int j; int? i;
j= i ?? 10 ; //j 為 10
3. readonly 與 const 的使用
1) readonly 是運行時常量
2)const 是編譯時常量,它是 static的,因此不能 寫為 static const int constval=100;
3)const 效率相對高。

4. 數值類型 與 引用類型的 相等
1)對于數值類型,如果兩者的值相等,則返回 true
2)對於引用類型,如果指向同一個對象(地址)則返回 true;
3)對於 string 類型,則例外,它是判斷值相等。

5. using 是 try{…} finally{ xx.Dispose();} 的語法魔棒,最后會將對象自動 Dispose()
6. 大多數情況下 用 foreach 代替 for , 因為語法更簡單,以及自動帶 dispose. 但是 如果遍歷的過程中對自身變量進行增刪除操作時,則不能用 foreach.
7. 儘量使用泛型集合List 代替非泛型集合ArrayList,ArrayList的元素是 object ,存在 裝箱拆箱的性能損耗。
8. Linq 中的延遲求值和主動求值
例如:

 List<int> list = new LIst<int>(){0,1,2,3,4,5,6,7};
 //延遲求值 
 var temp1 = form c in list where c> 5
                      select c;
  // 主動求值 
  var temp2 =from c in list where c > 5 
                      select c).ToList<int>();
   list[0] = 11;// 改變 list 某個元素的值
 //延遲求值 
 foreach (var item in temp1)
  {
    console.writeline(item.tostring()); //11,6,7
}
  // 主動求值 
 foreach(var item in temp2)
  {
   console.writeline(item.tostring());//6,7
}

9. IEnumerable 與 IQueryable 的區別
1)IEnumerable 相對適合于 Linq to object,對本地集合進行查詢,排序等,可以用自定義主法。
2)IQueryable 相對適合于Linq to sql , 對遠端資料進行查詢等操作。不可以用自定義方法。
10. 委派
委派有兩個要點。第一委派是方法指針;第二委派是一個類。
例子:

delegate int AddHandler(int i,int j );
delegate int PrintHandler(string msg);
static void Main(stirng[] args)
{
 AddHandler add = Add;
 PrintHandler print = Print;
 print(add(1,2).ToString());
}
static int Add(int i,int j)
{
 return i+j;
}
static void Print(string msg)
{
 console.writeline(msg);
}

注:上面的 委派定義 delegate 也可以直接用 Action,Fun 代替

static void Main(string[] args)
{
 Func<int,int,int> add = Add; // 第一個 int 參數是返回的類型,后面接著是參數類型
 Actoin<String> print =Print;//無返回值,直接帶參數類型
 print(add(1,2).ToString());
}

另: 一個Action委派加 并行的例子

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadSafeCollectionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            ConcurrentBag<string> bag = new ConcurrentBag<string>();
            Console.WriteLine($"当前包的元素个数【{bag.Count}】,是否为空【{bag.IsEmpty}】");
            Task task1 = Task.Run(() => 
            {
                bag.Add("北洛");
                bag.Add("云无月");
            });
            Task task2 = Task.Run(() =>
            {
                bag.Add("岑缨");
                bag.Add("姬轩辕");
            });
            Task.WaitAll(task1, task2);
            //包Bag支持添加重复的元素,和List一样都可以添加重复元素
            bag.Add("岑缨");
            Console.WriteLine($"当前包的元素个数【{bag.Count}】,是否为空【{bag.IsEmpty}】");
            Action action = new Action(() => 
            {
                string element;
                while (bag.TryTake(out element))
                {
                    Console.WriteLine($"取出元素:【{element}】,当前线程编号:【{Thread.CurrentThread.ManagedThreadId}】");
                }
            });
            //分成两部分取出
            Parallel.Invoke(action, action);
            string result;
            if (bag.TryPeek(out result))
            {
                Console.WriteLine($"包存在元素:【{result}】");
            }
            else
            {
                Console.WriteLine("包当前没有元素");
            }
            Console.WriteLine($"当前包的元素个数【{bag.Count}】,是否为空【{bag.IsEmpty}】");
            Console.ReadLine();
        }      

    }
}
 

11.C# 的virtual虛函數
1)定義:在基類中聲明virtural并在一個或多個子類中被重新定義的成員函數(override重寫)。
2)作用:實現類的多態性。
3)VIRTUAL虛函數與普通函數的區別:
普通函數在編譯時就靜態地編譯到執行文件中,其相對地址在程序運行期間不會發生變化。
虛函數在編譯期間不被靜態編譯,它的相對地址是不確定的,它會根據運行期對象實例去判斷要調用的函數,其中聲明時定義的類叫做聲明類,執行時實例化的類叫實例類。
4)使用
A. 當調用一個對象的函數時,系統先直接去檢查對象的聲明類,查看調用的函數時否為虛函數;
B. 如果不是虛函數,直接運行該函數;如果是虛函數,就去檢查實例類,檢查是否有重新實現(重寫OVERRIDE),如果有,馬上執行該重新實現的函數;如果沒有,則往上找父類,直到找到第一個重載該函數的父類的函數。
C# 一些學習小筆記及技巧_第1张图片
例子:

class program
{
  class A
  { 
      public A()
       {
          print();
       }
       public virtual void print(){}
  }
class B:A
{
  int x=1;int y;
  public B() {y=-1;}
  public override void print()
  {
     Console.WriteLine("x={0},y={1}",x,y);
  }
}
static void Main()
  {
     new B(); //輸出 x=1,y=0; 原因:創建子類時,會銜調用 父類的構造函數,然後再調用 子類的構造函數
  }
}
  1. 重寫(Override)與重載(OverLoad)的區別
    1)重寫(override): 子類重寫基類(父類)的方法。被重寫的方法必須是VIRTUAL虛方法,特點是3個相同:相同的方法名,相同的參數,相同的返回類別
    2)重載overload:同一個類中多個方法名稱相同,3 個特點:名稱 相同;參數和列表 不同,返回值類型可以不相同

  2. 建議使用PLINQ 代替 LINQ
    傳統LINQ是間線程的,PLINQ則是并發多線程的。
    例如:
    傳統LINQ,單線程,輸出有次序:

List<int> list = new List<int>(){0,1,2,3,4,5,6,7,8,9};
var query = form t in list select t;
foreach (var item in query)
{
  Console.WriteLine(item.ToStirng());//傳統LINQ,單線程,輸出有次序
}

PLINQ ,并發多線程,注意線程安全和輸出無次序

var query = from t in list.AsParallel() select t;
foreach(var item in query)
 {
  Console.WriteLine(item.ToStirng()); //輸出無次序
}
//如果要按次序,則可以使用:
var query = from t in list.AsParallel().AsOrdered() select t;

14. 使用 Parallel簡化使用 Task
Parallel在 system.Threading.Task中有3個方法

1)prarllel.For : 適用于數組
int[] nums = {1,2,3,4};
Parallel.For(0,nums.length,(i) =>{
  Console.WriteLine(nums[i].ToString()); //注:不一定按次序輸出
});

2) Parallel.ForEach : 適用于泛型集合
var list = New List<int>(){ 1,2,3,4,5};
Parallel.ForEach(list,(item)=>
{
  Console.WriteLine(item.ToString()); //注:不一定按次序輸出
});

3) Parallel.Invoke(()=>{ //隱式啟動輒 兩個 tasks.
  Console.WriteLine("task 1");
},
()=>{
Console.WriteLine("task 2");
});

15. 鎖 (LOCK)會讓多線程變成單線程,因為同時只允許一個線程訪問資源。
16. 用 params 減少函數的參數,例如:
void method(string,params int[] i);

未完(2022-04-25)

你可能感兴趣的:(基础知识,c#)