C#個人筆記


前言

記錄一下C#的一些東西,基礎好多還是不會,還是推薦微軟的官網文檔,網上的博客寫的都太水了,還是官網文檔好一點

微軟官方文檔

異步任務

同步方法的缺點

其實我最想講的就是這個,我舉個例子,有兩個方法,方法1和方法2,我現在想先執行方法1再執行方法2,如果我順序執行的話,那么必須等待方法1執行完成之后才執行方法2 代碼如下

static void Main(string[] args)
{
    method1();
    method2();
}

public static void method1()
{
    for (int i = 0; i < 80; i++)
    {
System.Console.WriteLine("method1: "+i);
    }
}
public static void method2() {
    for (int i = 0; i < 20; i++)
    {
System.Console.WriteLine("method2: "+i);
    }
}

執行一下就知道了,必須等待方法1執行完才會執行方法2,就比如我想燒水做飯,必須先等水燒開了我才能洗菜切菜......這明明是可以同時做的事情,我們可以使用異步方法解決

異步方法

這個分為兩種情況,I/O和CPU運算,我這里暫時沒用到I/O所以不寫了,講講CPU運算的

返回Task

static void Main(string[] args)
{
    method1();
    method2();
    System.Console.ReadKey();
}

public static async Task method1()
{
    await Task.Run(() =>
    {
 for (int i = 0; i < 80; i++)
 {
     System.Console.WriteLine("method1: " + i);
 }
    });
}
public static void method2() {
    for (int i = 0; i < 20; i++)
    {
 System.Console.WriteLine("method2: "+i);
    }
}

特點就是async,Task或者Task<T>,await,Task.Run這幾個

返回Task<T>

 static void Main(string[] args)
{
    callMethod();
    System.Console.ReadKey();
}

public static async void callMethod()
{
    Task<int> task = method1();
    int count = await task;
    method3(count);
}
public static async Task<int> method1()
{
    int count=0;
    await Task.Run(() =>
    {
 for (int i = 0; i < 80; i++)
 {
     System.Console.WriteLine("method1: " + i);
     count++;
 }
    });
    return count;
}
public static void method2()
{
    for (int i = 0; i < 20; i++)
    {
 System.Console.WriteLine("method2: " + i);
    }
}
public static void method3(int count)
{
    System.Console.WriteLine("Count is "+count);
}

C#讀取CSV,存入數據庫

C#讀取CSV的內容,以DataTable的格式返回

            string path = @"D:\360MoveData\Users\Justin\Desktop\dgkdata\Audio Products~Accessories.csv";


public static DataTable ReadData(string filePath)
        {
            //Encoding encoding = Common.GetType(filePath); //Encoding.ASCII;//
            Encoding encoding = Encoding.ASCII; //Encoding.ASCII;//

            DataTable dt = new DataTable();
            FileStream fs = new FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);

            //StreamReader sr = new StreamReader(fs, Encoding.UTF8);
            StreamReader sr = new StreamReader(fs, encoding);
            //string fileContent = sr.ReadToEnd();
            //encoding = sr.CurrentEncoding;
            //記錄每次讀取的一行記錄
            string strLine = "";
            //記錄每行記錄中的各字段內容
            string[] aryLine = null;
            string[] tableHead = null;
            //標示列數
            int columnCount = 0;
            //標示是否是讀取的第一行
            bool IsFirst = true;
            //逐行讀取CSV中的數據
            while ((strLine = sr.ReadLine()) != null)
            {
                if (IsFirst == true)
                {
                    tableHead = strLine.Split(',');
                    IsFirst = false;
                    columnCount = tableHead.Length;
                    //創建列
                    for (int i = 0; i < columnCount; i++)
                    {
                        DataColumn dc = new DataColumn(tableHead[i]);
                        dt.Columns.Add(dc);
                    }
                }
                else
                {
                    //MySplit這個方法看下面的介紹
                        List<string> dataList = MySplit(strLine);
                        aryLine = dataList.ToArray();
                    DataRow dr = dt.NewRow();
                    for (int j = 0; j < columnCount; j++)
                    {
                        dr[j] = aryLine[j];
                    }
                    dt.Rows.Add(dr);
                }
            }
            if (aryLine != null && aryLine.Length > 0)
            {
                dt.DefaultView.Sort = tableHead[0] + " " + "asc";
            }

            sr.Close();
            fs.Close();
            return dt;
        }

然后接受這個DataTable

//先獲取所有的列名
            DataTable dt = Read.ReadData(path);
            string[] strColumns = null;
            if (dt.Columns.Count > 0)
            {
                int columnNum = 0;
                columnNum = dt.Columns.Count;
                strColumns = new string[columnNum];
                for (int i = 0; i < dt.Columns.Count; i++)
                {
                    strColumns[i] = dt.Columns[i].ColumnName;
                }
            }


//在遍歷開始處理數據
            foreach (DataRow dataRow in dt.Rows)
            {
                foreach (var columsName in strColumns)
                {
                    switch (columsName)
                    {
                        case "Datasheets":
                            break;
                        case "Image":
                            break;
						處理邏輯......
                    }

                    string aaa = dataRow[columsName].ToString();
                    處理邏輯......
                    Console.WriteLine(aaa);
                }
            }

Split(',')過濾掉雙引號內的逗號

這個也可以叫做,C#讀取CSV文件逗號問題

我讀取的一串字符串是這樣的

"許嵩","蜀,雲泉",1,22,"音樂"

我使用Split(',')之后蜀雲泉就分開了,這顯然不是我要的結果

解決方法可以使用正則,但是我不會寫,所以寫一個最基礎的substring

        private static List<string> MySplit(string str)
        {
            const char mark = '"';
            const char comma = ',';

            bool startMark = false;
            int startIndex = -1;
            int endIndex = -1;

            List<string> myList = new List<string>();
            for (int i = 0; i < str.Length; i++)
            {
                if (str[0] == comma)
                {
                    myList.Add("");
                }
                if (startMark && str[i] == comma)
                {
                    continue;
                }
                if (str[i] == comma && i > 0)
                {
                    endIndex = i;
                }
                if (str[i] == mark && !startMark)
                {
                    startMark = true;
                }
                else if (str[i] == mark && startMark)
                {
                    startMark = false;
                }
                if (startIndex == -1)
                { startIndex = i; }
                if ((startIndex >= 0 && endIndex > 0) || (endIndex == -1 && i == str.Length - 1))
                {
                    if (endIndex == -1)
                    {
                        endIndex = i + 1;
                    }
                    myList.Add(str.Substring(startIndex, endIndex - startIndex));
                    startIndex = -1;
                    endIndex = -1;
                }
            }
            return myList;
        }

這個strLine就是C#讀取CSV的一行內容

用好代碼代替注釋

如開發人員發現需要寫注釋才能說清楚代碼塊的功用,應考慮重構,而不是洋洋灑灑寫一堆注釋。寫注釋來重復代碼本來就講得清的事情,只會變得臃腫,降低可讀性,還容易過時,因為將來可能更改代碼但沒有來得及更新注釋。

設計規范

  1. 不要使用注釋,除非代碼本身“一言難盡”。

  2. 要盡量寫清楚的代碼而不是通過注釋澄清復雜的算法。

C#的13種基元類型

所謂的基元類型,就是C#中的所有類型的基礎,分別有8種整數類型,2種小數類型,1種金融類型,1種布爾類型,1種字符類型:

金融類型是Decimal,布爾Bool,字符類型char

避免使用隱式類型

所謂的隱式類型就是var

var name = "許嵩";
string name = "許嵩";

我使用var或者string都是一樣的,在最終的CIL代碼里面也沒區別,但是,如果確定類型,還是直接指定類型好,一目了然

引用參數ref和輸出參數out

先說結論

  1. ref參數:將變量帶入一個方法中改變之后在帶出方法,ref參數使用前必須賦值

  2. out參數: 在返回多個值的時候使用out參數,使用前不需要賦值

舉個例子,代碼如下

        static void Main(string[] args)
        {
            int salary = 5000;

            jiangJin(salary);

            Console.WriteLine(salary);
            Console.Read();

        }

        static void jiangJin(int salary)

        {
            salary += 500;
        }

像這個例子,輸出的salary還是5000,雖然我經過了jiangJin方法的計算,但是我沒有return計算后的結果,所以不管方法內怎么計算了,只要不return,salary值沒變

現在我加一個ref就不同了

        static void Main(string[] args)
        {
            int salary = 5000;

            jiangJin(ref salary);

            Console.WriteLine(salary);
            Console.Read();

        }

        static void jiangJin(ref int salary)
        {
            salary += 500;
        }

我就加了一個ref,然后salary的輸出結果就是5500了,不需要return了

所以ref參數的作用是:將變量帶入一個方法中改變之后在帶出方法,以傳引用的方式來傳變量,而不是值拷貝的方式

out輸出參數其實和ref功能一模一樣,但是out輸出參數更注重檢查方法內是否對out參數進行賦值,代碼如下

        static void Main(string[] args)
        {
            int a = 1;
            int b;
            int asd = Calcu(a,out b);
            Console.WriteLine(asd + " : " + b);

            Console.Read();
        }
        static int Calcu(int a, out int b)

        {
            b = a;
            return a + b;
        }

泛型

復制代碼的麻煩

我現在寫一個類,如下

    public class StudyT

    {

        public void Add(string name)

        {
            Console.WriteLine("我是增加方法,變量是:" + name);
        }

    }

然后我可以實例化調用

 StudyT<string> studyT = new StudyT<string>();

 studyT.Add("許嵩");

但是我的Add方法,我希望string類型可以,int類型可以,float類型的也可以使用,那我怎么辦呢?

復制一下StudyT類,然后Add方法的參數類型改為int,這當然ok,但是麻煩

Object的裝箱拆箱損失性能

所以我選擇使用Object類型,如下

    public class StudyT

    {

        public void Add(object name)

        {
            Console.WriteLine("我是增加方法,變量是:" + name);
        }

    }

非常好,Object是基類,這下我傳入int,string,float都可以用,但是又來了一個新問題,Object轉化的時候有裝箱拆箱,損失性能了,而且還有賦值不檢查類型的錯誤可能,所以,我選擇使用泛型

泛型的好處

    public class StudyT<T> 

    {

        public void Add(T name)
        {
            Console.WriteLine("我是增加方法,變量是:" + name);
        }

    }

泛型的使用方法就是

  1. 類后加

  2. 方法類型使用T表示

這下我實例化對象調用的時候,傳入什么類型,就是什么類型,而且還有類型檢查,很安全

泛型的約束

我這個方法啊,只希望某個類或者某個接口才能使用,你給我傳入一個int,string類型的沒用,所以我做個約束,你傳入的類型,必須是我想要的指定類型

    public class StudyT<T> where T : IMovie

    {

        public void Add(T name)
        {
            Console.WriteLine("我是增加方法,變量是:" + name);
        }

    }

也很簡單,直接 where T : Movie 即可,表明,傳入的類型必須是繼承了IMovie接口的,不管是大電影,微電影,動畫片,科幻片啥的,只要繼承了IMovie接口就能使用

委托

我終於知道委托和事件是干嘛的了,多虧我同學寫的demo,不然我還是不理解委托

書上說委托可以解決大量if else的情況,百科也是這樣說的,但是我沒啥感覺,出了一個排序的例子,我沒感覺其他例子可以解決大量if else的,暫時不管這個了

對於委托最好的理解和使用就是發布訂閱模式了,在設計模式里面也稱之為觀察者模式

發布訂閱模式

這個例子很清楚的講解了委托的使用,我有3個類,服務器,客戶端,消息管理類,代碼如下

    class Server
    {
        public void PublishInfo(string info)
        {
            Console.WriteLine($"服務器發布了新消息: {info}");
            InformationManager.instance.Info = info;
            InformationManager.instance.UpdateInformation?.Invoke();

        }
    }

    class Client
    {
        string clientName = "";
        public Client(string name,bool isSub = false)
        {
            clientName = name;
            if (isSub)
            {
                InformationManager.instance.UpdateInformation += ReceiveInfo;
            }
        }

        public void ReceiveInfo()
        {
            Console.WriteLine($"{clientName}用戶收到了消息: {InformationManager.instance.Info}");
        }
    }

    class InformationManager
    {
        private string mInfo;
        public Action UpdateInformation;

        //單例的消息管理器實例
        private static InformationManager _instance;
        public static InformationManager instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new InformationManager();
                }
                return _instance;
            }
        }
    }

然后Main方法調用如下

 Server server = new Server();
 Client client = new Client("劉備",true);

 Client client1 = new Client("關羽");

 Client client2 = new Client("張飛");

 server.PublishInfo("好消息,許嵩發新歌啦");

結果很Nice,這就是委托了

不安全的委托

我們在給委托添加方法的時候,使用的是+=

InformationManager.instance.UpdateInformation += ReceiveInfo;

但是有時候我們會不小心寫成=,這樣訂閱者就會被覆蓋,我有3個訂閱者,結果寫成了=,只有第3個訂閱者收到消息了,前兩個被覆蓋了,這樣很不好.

不要說你會小心的,你不會忘記寫+=,這是無法避免的事情,因為我剛學的時候也總是忘記寫成=號,這就是不安全的委托,所以我們需要修改一下,使用事件解決這個問題

事件,就是安全的委托

事件:安全的委托

上面說了,委托方法的+=很容易被寫成=,這樣不安全,所以我們改一下代碼,使用事件,事件是安全的委托,因為事件強制你寫+=

    class Server
    {
        public void PublishInfo(string info)
        {
            Console.WriteLine($"服務器發布了新消息: {info}");
            InformationManager.instance.Info = info;
            //InformationManager.instance.UpdateInformation?.Invoke(); 如果是委托需要調用
        }
    }
    class Client

    {
        string clientName = "";
        public Client(string name,bool isSub = false)
        {
            clientName = name;
            if (isSub)
            {
                //這里的委托必須是+=,寫成=就覆蓋了,雖然我知道,但是我又忘了,所以寫成事件,事件強制+=,所以事件是安全的委托
                InformationManager.instance.UpdateInformation += ReceiveInfo;
            }
        }

        public void ReceiveInfo()
        {
            Console.WriteLine($"{clientName}用戶收到了消息: {InformationManager.instance.Info}");
        }
    }
    class InformationManager

    {
        private string mInfo;
        //public Action UpdateInformation; 委托
        public event Action UpdateInformation;

        //單例的消息管理器實例
        private static InformationManager _instance;
        public static InformationManager instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new InformationManager();
                }
                return _instance;
            }
        }

        /// <summary>
        /// 這個方法是事件的時候才用的,委托是直接調用,事件是觸發,所以觸發
        /// </summary>
        public string Info
        {
            get => mInfo;
            set
            {
                if (value != mInfo)
                {
                    mInfo = value;
                    if (UpdateInformation != null)
                    {
                        UpdateInformation();
                    }
                }
            }
        }

    }

委托需要調用,而事件是用來觸發的,所以在InformationManager加了一個觸發事件

這次再給委托添加方法的時候試試,必須寫成+=,這樣就再也不怕寫成=號了

反射

            //反射第一種:GetType()  有實例對象,可以調用獲取屬性,方法,字段
            DateTime dateTime = new DateTime();
            Type type = dateTime.GetType();
            PropertyInfo[] propertyInfos = type.GetProperties(); //所有的屬性
            MethodInfo[] methodInfos = type.GetMethods(); //所有的方法
            FieldInfo[] fieldInfos = type.GetFields(); //所有的字段

            //反射第二種:typeof()   沒有實例對象的情況,比如靜態類或單純的類名
            Type type1 = typeof(X);
            PropertyInfo[] xpropertyInfos = type1.GetProperties(); //所有的屬性
            MethodInfo[] xmethodInfos = type1.GetMethods(); //所有的方法
            FieldInfo[] xfieldInfos = type1.GetFields(); //所有的字段

            Type type2 = typeof(StudyThread);
            MethodInfo[] tmethodInfos = type2.GetMethods(); //所有的方法
            StudyThread studyThread = (StudyThread)Activator.CreateInstance(type2);//Activator是根據Type獲取實例對象
            studyThread.Test();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM