C#入門01:開胃菜-語法基礎

輸入和輸出

//控制檯輸入整數和字符串
    int age = int.Parse(Console.ReadLine());
    string name = Console.ReadLine();

    //控制檯輸入浮點數
    double salary = double.Parse(Console.ReadLine());
    double addSalary = Convert.ToDouble(Console.ReadLine());

    //控制檯格式化輸出
    Console.WriteLine("這是第一個C#控制檯程序");
    Console.WriteLine("Your name is {0}, age is {1}, and salary is {2:f2}, addSalary is {3}",
        name, age, salary, addSalary.ToString());

    //格式化到字符串
    int age = 32;
    double salary = 12000.59;
    String name = "zhangsan";
    String outstr = String.Format("name={0}, age={1}, salary={2:f1}", name, age, salary);
    Console.WriteLine(outstr);

    //輸出日期和時間,DateTime.Today只獲取日期
    Console.WriteLine("{0:D} {0:t}", DateTime.Now);
    DateTime dt = new DateTime(2017, 4, 1, 13, 16, 32, 108);
    dt.ToString("y yy yyy yyyy");//17 17 2017 2017
    dt.ToString("M MM MMM MMMM");//4  04 四月 四月
    dt.ToString("d dd ddd dddd");//1  01 週六 星期六
    dt.ToString("t tt");//下 下午
    dt.ToString("H HH");//13 13
    dt.ToString("h hh");//1  01
    dt.ToString("m mm");//16 16
    dt.ToString("s ss");//32 32

數據類型

C#中的數據類型包含值類型(int,double,float,string等),引用類型(object,dynamic和string三種內置的引用類型,以及class等)和指針類型(C#中不建議使用指針類型)。數組

**如何區別值類型和引用類型? **
值類型:能夠直接賦值的一般是值類型,好比int,float等,String類型除外,它是特殊的引用類型;
引用類型:一般是對象,好比從Object派生的對象,能夠設置爲null,還須要使用new操做符來申請內存空間構造對象;好比本身實現的class,系統定義的class等數據結構

類型檢查
sizeof()判斷類型的大小,判斷值類型有效,因爲在C#中引用類型相似C++中的引用,不存儲內存空間,所以不能用sizeof()計算引用類型的空間大小;
typeof()返回數據的類型app

內置的引用類型函數

//對象(Object)類型,裝箱就是這裏將值類型轉換爲引用類型,反之就是拆箱
    object obj1 = 100;
    object obj2 = 100.5;
    Console.WriteLine("obj1 is {0}, obj2 is {1}", obj1.GetType().ToString(), obj2.GetType().ToString());

    //動態(Dynamic)類型,能夠是值類型,也能夠是引用自定義class類型
    dynamic obj3 = 100;
    dynamic obj4 = 100.5;
    Console.WriteLine("obj3 is {0}, obj4 is {1}", obj3.GetType().ToString(), obj4.GetType().ToString());

    //字符串類型,使用@將轉義字符(\)看成普通字符對待
    string str1 = @"C:\Windows";
    string str2 = "C:\\Windows";
    if (str1 == str2)
    {
        Console.WriteLine("他們相等");
    }

動態類型與對象類型類似,可是對象類型變量的類型檢查是在編譯時發生的,而動態類型變量的類型檢查是在運行時發生的。指針

用戶自定義引用類型
用戶或系統實現的class、interface 或 delegate等;日誌

類型轉換

顯示和隱式轉換
等同於C++的操做code

int v = 10;    
    double d = v;        //隱式轉換
    int v2 = (int)d;      //顯式轉換

AS操做(引用類型轉換)orm

//子類到基類的轉換,兩種方法均可以
    //第一種是強制轉換,在編譯期間會進行判斷
    //第二種轉換在失敗時base b會爲空,不拋出異常
    base b = subclass;
    base b = subclass as base;

    //基類到派生類的轉換:
    //C++:subclass* sub = dynamic_cast<base>(base);
    //if (sub != nullptr) 須要作一下判斷是否轉換成功
    Rectangle rect2 = sh as Rectangle;
    if (rect2 != null)
    {
        //須要作一下判斷,轉換失敗rect2爲空,但不會拋出異常
    }

使用AS操做符轉換,可是AS只能用於引用類型和可爲空的類型。使用as有不少好處,當沒法進行類型轉換時,會將對象賦值爲NULL,避免類型轉換時報錯或是出異常。C#拋出異常在進行捕獲異常並進行處理是很消耗資源的,若是隻是將對象賦值爲NULL的話是幾乎不消耗資源的(消耗很小的資源)。對象

裝箱和拆箱(值類型和引用類型轉換)繼承

object obj1 = 100;
    object obj2 = 100.5;
    Console.WriteLine("obj1 is {0}, obj2 is {1}", obj1.GetType().ToString(), obj2.GetType().ToString());

裝箱和拆箱在值類型和引用類型之間架起了一座橋樑,使得任何 value-type 的值均可以轉換爲 object 類型的值,反過來轉換也能夠。 裝箱:裝箱是指將一個值類型的數據隱式地轉換成一個對象類型(object)的數據。執行裝箱操做時不可避免的要在堆上申請內存空間,並將堆棧上的值類型數據複製到申請的堆內存空間上,這確定是要消耗內存和cpu資源的。注意:在執行裝箱轉換時,也可使用顯式轉換。 拆箱:拆箱是指將一個對象類型的數據顯式地轉換成一個值類型數據。拆箱過程是裝箱的逆過程,是將存儲在堆上的引用類型值轉換爲值類型並賦給值類型變量。拆箱操做分爲兩步:一是檢查對象實例,確保它是給定值類型的一個裝箱值;而是將該值從實例複製到值類型變量中。裝箱和拆箱都是要消耗內存和cpu資源的,也就形成效率下降,因此要儘可能避免使用。

可空類型

//num1不能爲空值,可是可使用?符號來定義可空值類型
    double? num1 = null;
    double? num2 = 3.14157;
    double num3;
    // num1 若是爲空值則返回 5.34
    num3 = num1 ?? 5.34; 
    Console.WriteLine("num3 的值: {0}", num3);
    num3 = num2 ?? 5.34;
    Console.WriteLine("num3 的值: {0}", num3);
    Console.ReadLine();

一維數組

//聲明數組
    int[] age;
    double[] balance;

    //初始化數組並賦值
    age = new int[5] { 1, 2, 3, 4, 5 };
    //也能夠不指定長度:age = new int[] { 1, 2, 3, 4, 5 };

    //初始化數組使用默認值0
    balance = new double[10];

    //訪問數組元素
    int val = age[3];

    //遍歷數組元素
    foreach (int v in age)
    {
        Console.WriteLine("{0}", v);
    }

多維數組

//聲明數組
    int[,] age;
    double[,] balance;

    //初始化數組並賦值
    age = new int[2,3] { { 1, 2, 3 }, { 4, 5, 6 } };
    //也能夠不指定長度:age = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } };

    //初始化數組使用默認值0
    balance = new double[5, 10];

    //訪問數組元素
    int val = age[0,1];

    //遍歷數組元素
    foreach (int v in age)
    {
        Console.WriteLine("{0}", v);
    }

交錯數組

交錯數組是數組的數組,不一樣於多維數組。多維數組本質上元素都是一個數據類型,而交錯數組的元素本質上是另一個數組。交錯數組和C++的多維數組很相似,聲明和使用看起來都同樣,要特別注意!

//聲明數組
    int[][] scores;

    //初始化數組
    scores = new int[5][];
    for (int i = 0; i < scores.Length; i++)
    {
        //數組元素是一個有4個元素的數組
        scores[i] = new int[4] { i * i + 1, i * i + 2, i * i + 3, i * i + 4 };
    }
    //也能夠直接初始化:scores = new int[2][] { new int[] { 92, 93, 94 }, new int[] { 85, 66, 87, 88 } };

    //訪問數組元素
    int val = scores[0][2];

    //遍歷數組元素
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            Console.WriteLine("a[{0}][{1}] = {2}", i, j, scores[i][j]);
        }
    }

Array 類

Array 類是 C# 中全部數組的基類,它是在 System 命名空間中定義。
Array 類提供了各類用於數組的屬性和方法。
Array類是一個抽象類,不能被實例化。

結構體Struct

struct Books
    {
        public string title;
        public string author;
    };

    Books b;
    b.title = "Eleven";
    b.author = "ZhangSan";
    Console.WriteLine("{0}, {1}", b.title, b.author);

在 C# 中,結構體是值類型數據結構。它使得一個單一變量能夠存儲各類數據類型的相關數據。

C# 中的結構有如下特色:

  • 結構可帶有方法、字段、索引、屬性、運算符方法和事件。
  • 結構可定義構造函數,但不能定義析構函數。可是,您不能爲結構定義默認的構造函數。默認的構造函數是自動定義的,且不能被改變。
  • 與類不一樣,結構不能繼承其餘的結構或類。
  • 結構不能做爲其餘結構或類的基礎結構。
  • 結構可實現一個或多個接口。
  • 結構成員不能指定爲 abstract、virtual 或 protected。
  • 當您使用 New 操做符建立一個結構對象時,會調用適當的構造函數來建立結構。與類不一樣,結構能夠不使用 New 操做符便可被實例化。
  • 若是不使用 New 操做符,只有在全部的字段都被初始化以後,字段才被賦值,對象才被使用。

類和結構有如下幾個基本的不一樣點:

  • 類是引用類型,結構是值類型。
  • 結構不支持繼承。
  • 結構不能聲明默認的構造函數。

枚舉Enum

C# 枚舉是值類型。換句話說,枚舉包含本身的值,且不能繼承或傳遞繼承。

//默認從0開始,依次加1
    enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat };

    //指定從10開始,中間Fri指定爲20,Sat就是21
    enum Days2 { Sun = 10, Mon, tue, Wed, thu, Fri=20, Sat };

    //和C++不同,這裏會打印字符串Sun,Fri,Sat
    Console.WriteLine("{0}, {1}, {2}", Days.Sun, Days.Fri, Days.Sat);
    Console.WriteLine("{0}, {1}, {2}", Days2.Sun, Days2.Fri, Days2.Sat);

    //強制將枚舉轉換爲int,纔會獲得和C++同樣的結果
    Console.WriteLine("{0}, {1}, {2}", (int)Days.Sun, (int)Days.Fri, (int)Days.Sat);
    Console.WriteLine("{0}, {1}, {2}", (int)Days2.Sun, (int)Days2.Fri, (int)Days2.Sat);

參數傳遞

和C++同樣,C#也有值傳遞和引用傳遞,可是比C++多一個參數傳出功能。

//引用傳遞使用ref而不是&
    public void swap(ref int x, ref int y)
    {
        int temp = x;
        x = y;
        y = temp;
    }

    //輸出參數傳遞,對於未初始化的參數獲取頗有用
    public void GetValues(out int a, out int b)
    {
        Console.WriteLine("請輸入第一個值: ");
        a = Convert.ToInt32(Console.ReadLine());
        Console.WriteLine("請輸入第二個值: ");
        b = Convert.ToInt32(Console.ReadLine());
    }

    int x = 100;
    int y = 200;
    MyClass n = new MyClass();

    n.swap(ref x, ref y);
    Console.WriteLine("在交換以後,x 的值: {0}", x);
    Console.WriteLine("在交換以後,y 的值: {0}", y);

    int a;
    int b;
    n.GetValues(out a, out b);
    Console.WriteLine("a={0}, b={1}", a, b);
    //這裏不能使用swap,由於a,b都沒有初始化
    //n.swap(ref a, ref b);

C#的引用傳遞可使用值傳遞也能夠顯式使用引用傳遞,效果是同樣的。
C#將Object obj2 = obj1當成對象引用,而不是建立新的對象,建立新對象都使用new操做符。而C++對象引用必須強制使用&符號,好比:Object& obj2 = obj1。這點和C++有很大區別!

class MyData
    {
        public int val = 0;
        double v2 = 100.3232;

        //經過傳遞引用改變數據
        static public void ChangeValue1(ref MyData d)
        {
            d.val = 60;
        }

        //經過傳遞值改變數據,由於這個值是一個引用類型,所以能夠改變數據
        //若是參數是值類型,如int,float等就不能夠
        static public void ChangeValue2(MyData d)
        {
            d.val = 100;
        }
    }

    //使用new建立對象
    MyData v = new MyData();
    MyData.ChangeValue1(ref v);
    Console.WriteLine("v.val={0}", v.val);
    MyData.ChangeValue2(v);
    Console.WriteLine("new v.val={0}", v.val);

C#數組也是對象,所以參數傳遞中的數組等同於引用傳遞:

//也能夠static void init(ref int[] array)這樣聲明  
    static void init(int[] array)  
    {
        int index = 0;
        int size = array.Length;
        for (int i=0; i<size; i++)
        {
            array[i] = ++index;
        }
    }

    //建立一個10個元素長度的數組
    int[] array = new int[10];

    //使用參數傳遞初始化數組,等同於Program.init(ref array);   
    Program.init(array);

    //打印數組元素
    foreach (int i in array)
    {
        Console.Write("{0} ", i);
    }

可變參數(參數數組)

相似於C/C++的可變參數(不定長參數),C# 經過使用參數數組來實現不可知個數的參數傳遞。
1.帶 params 關鍵字的參數類型必須是一維數組,不能使用在多維數組上;
2.不容許和 ref、out 同時使用;
3.帶 params 關鍵字的參數必須是最後一個參數,而且在方法聲明中只容許一個 params 關鍵字。
4.不能僅使用 params 來使用重載方法。
5.沒有 params 關鍵字的方法的優先級高於帶有params關鍵字的方法的優先級

//參數數組只能傳遞int類型
    public static void UseParams(params int[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }

    //能夠傳遞任意object類型
    public static void UseParams2(params object[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }

    UseParams(1, 2, 3, 4);
    UseParams2(1, 'a', "Apple");
    UseParams2();   //不填入參數

    //和UseParams(1, 2, 3, 4);等價
    int[] myIntArray = { 5, 6, 7, 8, 9, };
    UseParams(myIntArray);

    object[] myObjArray = { 1, 'b', "boom", "app" };
    UseParams2(myObjArray);

    //輸出爲"System.Int32[]",這裏把數組看做爲一個object類型了
    UseParams2(myIntArray);

Const和Readonly

靜態常量
所謂靜態常量就是在編譯期間會對變量進行解析,再將常量的值替換成初始化的值。
動態常量
所謂動態常量就是編譯期間會將變量標記只讀常量,而不用常量的值代替,這樣在聲明時能夠不初始化,能夠延遲到構造函數初始化。

const修飾的常量是上述中的第一種,即靜態常量,而readonly是上述中第二種即動態常量。他們的區別能夠從靜態常量和動態常量的特性來講明:

  • const修飾的常量在聲明時必須初始化值;readonly修飾的常量能夠不初始化值,且能夠延遲到構造函數。
  • cons修飾的常量在編譯期間會被解析,並將常量的值替換成初始化的值;而readonly延遲到運行的時候。
  • const修飾的常量注重的是效率;readonly修飾的常量注重靈活。
  • const修飾的常量沒有內存消耗;readonly由於須要保存常量,因此有內存消耗。
  • const只能修飾基元類型、枚舉類、或者字符串類型;readonly卻沒有這個限制。

預處理

和C++同樣,C#也能夠定義一系列預處理指令。能夠在項目屬性中設置預處理指令: 輸入圖片說明
勾選DEBUG和TRACE常量會定義這兩個預約義指令,在條件編譯符號中還能夠自行定義須要的預處理指令,用空格分開。
另外還能夠在代碼文件的using指令前定義預處理指令:

#define GPU
using System;
...
#if GPU
            Console.WriteLine("Use GPU");
#else 
#warning Only Support GPU !
#endif

這裏使用#warning在編譯時產生一個編譯警告(還能夠#error產生編譯錯誤)。

代碼摺疊

另外比較經常使用的就是代碼段管理預處理指令,在IDE中能夠摺疊被#region包圍的代碼:

#region 私有成員
    private int _width = 0;
    private int _height = 0;
    #endregion

    #region 公有方法
    public void Submit()
    {

    }
    public static void Add(MyClass A, MyClass B)
    {

    }
    #endregion

這樣一來就能夠摺疊這段代碼,若是沒有顯示+號,就須要在菜單->編輯->大綱顯示,開啓代碼的大綱顯示功能。

異常處理

C#提供了很豐富的異常處理機制,這點和C++很類似,連關鍵字try,catch,finally都是如出一轍。整體上分爲兩類異常:SystemException和ApplicationException。
下面的代碼展現自定義異常類的最多見用法:

public class MyClass
    {
        public int Work(int a, int b)
        {
            if (b == 0)
            {
                throw new MyException("除數不能爲零!");
            }
            return a / b;
        }
            
    }

    //自定義異常處理類,從ApplicationException派生
    public class MyException : ApplicationException
    {
        public MyException(string messag) : base(messag)
        {

        }
    }

    try
    {
        MyClass my = new MyClass();
        int value = my.Work(100, 0);
        Console.WriteLine("value is {0}", value);
    }
    catch (MyException e)
    {
        //捕獲自定義異常
        Console.WriteLine("MyException : {0}", e.Message);
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception : {0}", e.Message);
        //若是不想處理,仍然能夠繼續拋出異常
        throw e;
    }
    finally
    {
        //不管是否拋出異常都會執行這段代碼
        Console.WriteLine("程序退出");
    }

文件輸入和輸出

一、通用文件流FileStream操做,便可寫文件也可讀取文件

//打開文件流
    FileStream F = new FileStream("test.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite);

    //寫文件數據
    for (int i = 1; i <= 20; i++)
    {
        F.WriteByte((byte)i);
    }

    //讀取文件流
    F.Position = 0;
    for (int i = 0; i <= 20; i++)
    {
        Console.Write(F.ReadByte() + " ");
    }

    //關閉文件
    F.Close();

二、簡單文本文件的寫入StreamWriter和讀取StreamReader

//寫文本文件
    string[] names = new string[] { "Zara Ali", "Nuha Ali" };

    //使用using也能夠關閉文件
    using (StreamWriter sw = new StreamWriter("names.txt"))
    {
        foreach (string s in names)
        {
            sw.WriteLine(s);
        }
    }

    //讀取文本文件
    string line = "";
    using (StreamReader sr = new StreamReader("names.txt"))
    {
        while ((line = sr.ReadLine()) != null)
        {
            Console.WriteLine(line);
        }
    }

三、二進制文件的讀BinaryReader和寫BinaryWriter

try
    {
        //從控制檯輸入數據並寫入二進制文件
        BinaryWriter bw = new BinaryWriter(new FileStream("mydata", FileMode.Create));
        Int32 vInt = Convert.ToInt32(Console.ReadLine());
        Double vDoub = Convert.ToDouble(Console.ReadLine());
        String vStr = Console.ReadLine();
        bw.Write(vInt);
        bw.Write(vDoub);
        bw.Write(vStr);
        bw.Close();
    }
    catch (IOException e)
    {
        Console.WriteLine(e.Message);
        return;
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
        return;
    }

    try
    {
        // 讀取二進制文件
        BinaryReader br = new BinaryReader(new FileStream("mydata", FileMode.Open));
        Console.WriteLine("Read:\n{0}\n{1}\n{2}", br.ReadInt32(), br.ReadDouble(), br.ReadString());
        br.Close();
    }
    catch (IOException e)
    {
        Console.WriteLine(e.Message);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }

文件系統的操做

DirectoryInfo 類
DirectoryInfo 類派生自 FileSystemInfo 類。它提供了各類用於建立、移動、瀏覽目錄和子目錄的方法。該類不能被繼承。

**FileInfo 類 **
FileInfo 類派生自 FileSystemInfo 類。它提供了用於建立、複製、刪除、移動、打開文件的屬性和方法,且有助於 FileStream 對象的建立。該類不能被繼承。

下面是一個自動刪除過時日誌的函數,使用了遞歸遍歷所有文件:

void DeleteLog(string folderFullName, int preDays = 14)
{
    DateTime timeUp = DateTime.UtcNow.AddDays(-preDays);
    Console.WriteLine("Will delete old log befor {0}===>", timeUp.ToString());

    DirectoryInfo TheFolder = new DirectoryInfo(folderFullName);
    foreach (DirectoryInfo NextFolder in TheFolder.GetDirectories())
    {
        //遍歷子文件夾
        DeleteLog(NextFolder.FullName);
    }

    foreach (FileInfo fileName in TheFolder.GetFiles())
    {
        //刪除指定時間以前的文件
        if (DateTime.Compare(timeUp, fileName.LastWriteTime) >= 0)
        {
            Console.WriteLine(fileName.FullName);
            File.Delete(fileName.FullName);
        }
    }
}
相關文章
相關標籤/搜索