1. 程式人生 > >大話C#語言——基礎知識總結

大話C#語言——基礎知識總結

推薦閱讀:

時隔半年沒用C#了,最近打算回來鞏固一下,下面就著重一些主要的,易忘的,難點的知識點帶領大家一起回顧這個語言。

建立C#專案

開啟VS編輯器,檔案-新建-專案-控制檯應用程式-命名為“CSharp學習”-開啟專案如下:

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

namespace CSharp學習
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

原則來說一個 C# 程式主要包括以下部分:

(1)名稱空間宣告(Namespace declaration)
(2)一個 class
(3)Class 方法
(4)Class 屬性
(5)一個 Main 方法
(6)語句(Statements)& 表示式(Expressions)
(7)註釋

完善程式如下:

using System;
namespace HelloWorldApplication
{
   class HelloWorld
   {
      static void Main(string[] args)// C# 程式的 入口點
      {
         /* 我的第一個 C# 程式*/
         Console.WriteLine("Hello World");
         Console.ReadKey();//使得程式會等待一個按鍵的動作,防止程式從 Visual Studio .NET 啟動時螢幕會快速執行並關閉。
      }
   }
}

注意:在任何 C# 程式中的第一條語句都是:using System;
下面給大家介紹一個技巧:輸出語句的快捷鍵:“cw” + Tab + Tab

變數型別

值型別(Value types):從類 System.ValueType 中派生的,比如 int、char、float。
引用型別(Reference types):指的是一個記憶體位置,內建的引用型別有:object、dynamic、string。使用者自定義引用型別有:class、interface 或 delegate
指標型別(Pointer types):儲存另一種型別的記憶體地址

物件(Object)型別:當一個值型別轉換為物件型別時,則被稱為 裝箱

object obj;
obj = 100; // 這是裝箱

當一個物件型別轉換為值型別時,則被稱為 拆箱

這裡有個常見的筆試題

什麼是裝箱和拆箱?

裝箱:將值型別轉換為引用型別。
拆箱:將引用型別轉換為值。

接收使用者輸入

Console.ReadLine();

返回資料型別的大小

sizeof()

返回 class 的型別

typeof()

? : 運算子

可以用來替代 if…else 語句
格式:Exp1 ? Exp2 : Exp3;
說明:? 表示式的值是由 Exp1 決定的。如果 Exp1 為真,則計算 Exp2 的值,結果即為整個 ? 表示式的值。如果 Exp1 為假,則計算 Exp3 的值,結果即為整個 ? 表示式的值。
例如:

int a=1;
int b=2;
int c;
a>b?c=a+b;c=b-a;

迴圈語句foreach

格式:foreach (int element in arr){}

注意:foreach只能一一取得陣列中的元素,並不能利用這種語句改變陣列所儲存的元素。

封裝

把一個或多個專案封閉在一個物理的或者邏輯的包中,通過 訪問修飾符實現。

訪問修飾符分為:

public:所有物件都可以訪問;
private:物件本身在物件內部可以訪問;
protected:只有該類物件及其子類物件可以訪問
internal:同一個程式集的物件可以訪問;
protected internal:訪問限於當前程式集或派生自包含類的型別。

方法中的引數型別

(1)值引數:這種方式複製引數的實際值給函式的形式引數,實參和形參使用的是兩個不同記憶體中的值。在這種情況下,當形參的值發生改變時,不會影響實參的值,從而保證了實引數據的安全。
(2)引用引數:這種方式複製引數的記憶體位置的引用給形式引數。這意味著,當形參的值發生改變時,同時也改變實參的值。在 C# 中,使用 ref 關鍵字宣告引用引數
(3)輸出引數:這種方式可以返回多個值。

下面就引用引數舉個例子:
定義一個方法:

	public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* 儲存 x 的值 */
         x = y;    /* 把 y 賦值給 x */
         y = temp; /* 把 temp 賦值給 y */
       }

呼叫該方法,實現a與b的值互換:

 swap(ref a, ref b);

大家都知道return 語句可用於只從函式中返回一個值,那麼需要返回多個值時,就得用到第三種類型了。特別的:當需要從一個引數沒有指定初始值的方法中返回值時,輸出引數特別有用
下面舉個例子:
定義一個方法

	public void getValues(out int x, out int y )
      {
          Console.WriteLine("請輸入第一個值: ");
          x = Convert.ToInt32(Console.ReadLine());
          Console.WriteLine("請輸入第二個值: ");
          y = Convert.ToInt32(Console.ReadLine());
      }

呼叫該方法:

 		 int a , b;
         
         /* 呼叫函式來獲取值 */
         n.getValues(out a, out b);

下面來說說一個經常出現的面試題:

ref和out的異同?

同:當一個方法需要返回多個值的時候,就需要用到ref和out。
異:ref 要求變數必須在傳遞之前進行初始化。
結論:關鍵字“ref“和”out”之間的唯一區別就是關鍵字”out“不要求呼叫程式碼初始化要傳遞的引數值。那麼關鍵字‘ref”什麼時候用呢?當您需要確保呼叫方法已經初始化引數值的時候,您就應該使用關鍵字“ref”。

可空型別nullable:?,??

下面舉個例子來幫助大家更直觀的理解:

		 double? num1 = null;//空
         double? num2 = 3.14157;//3.14157
         double? num3 = new double?();//空
         double num4;
         num4 = num1 ?? 5.34;      // num1 如果為空值則返回 5.34

由於num1 為空,所以num4=5.34。

陣列

1.一維陣列:
宣告一個沒有初始值的陣列:

int[] num;

宣告一個指定長度的陣列:

int[] num= new num[10];

建立並初始化一個數組
方法1:

int num={1,2,3,4,5}

方法2:

int num=[5]{1,2,3,4,5}

方法3:

int num=[]{1,2,3,4,5}

2.多維陣列:
宣告一個二位陣列:

int num[,]

建立並初始化一個二維陣列

int[,] a = new int[5, 2] {{0,0}, {1,2}, {2,4}, {3,6}, {4,8} };

3.交錯陣列:陣列的陣列
宣告:

int [][] scores

4.引數陣列:當宣告一個方法時,不確定要傳遞給函式作為引數的引數數目,使用 關鍵字:params
例如:
定義一個方法:

	  public int AddElements(params int[] arr)
      {
         int sum = 0;
         foreach (int i in arr)
         {
            sum += i;
         }
         return sum;
      }

使用該方法:

		 ParamArray app = new ParamArray();
         int sum = app.AddElements(512, 720, 250, 567, 889);

不使用引數params時程式碼如下:
定義一個方法

	  public int AddElements(int[] arr)
      {
         int sum = 0;
         foreach (int i in arr)
         {
            sum += i;
         }
         return sum;
      }

使用該方法:

 		 int[] num={1,2,3};
         ParamArray app = new ParamArray();
         int sum = app.AddElements(num);

5.Array 類:是 C# 中所有陣列的基類,它是在 System 名稱空間中定義

結構體struct

結構體是值型別資料結構,可以儲存各種資料型別的相關資料。與類不同,結構不能繼承其他的結構或類;結構可實現一個或多個介面;結構成員不能指定為 abstract、virtual 或 protected。

類和結構的不同點:

1.類是引用型別,結構是值型別。
2.結構不支援繼承。
3.結構不能宣告預設的建構函式。
4.結構體中宣告的欄位無法賦予初值,類可以。

類與結構的選擇

首先明確,的物件是儲存在空間中,結構儲存在中。堆空間大,但訪問速度較慢,棧空間小,訪問速度相對更快。故而,當我們描述一個輕量級物件的時候,結構可提高效率,成本更低。當然,這也得從需求出發,假如我們在傳值的時候希望傳遞的是物件的引用地址而不是物件的拷貝,就應該使用類了。

列舉enum

列舉是值型別;列舉包含自己的值,且不能繼承或傳遞繼承

類class

象是類的例項。構成類的方法和變數成為類的成員。
類的建構函式是類的一個特殊的成員函式,當建立類的新物件時執行。
預設的建構函式沒有任何引數。如果需要一個帶有引數的建構函式,這種建構函式叫做引數化建構函式。這種技術可以幫助你在建立物件的同時給物件賦初始值,例如:

   lass Line
   {
      private double length;   // 線條的長度
      public Line(double len)  // 引數化建構函式
      {
         Console.WriteLine("物件已建立,length = {0}", len);
         length = len;
      }
   }

解構函式是在類的名稱前加上一個波浪形(~)作為字首,它不返回值,也不帶任何引數。解構函式用於在結束程式(比如關閉檔案、釋放記憶體等)之前釋放資源。解構函式不能繼承或過載。
類的靜態成員:當我們宣告一個類成員為靜態時,意味著無論有多少個類的物件被建立,只會有一個該靜態成員的副本。例如:
宣告一個類:

	class StaticVar
    {
       public static int num;
        public void count()
        {
            num++;
        }
    }

使用類中的count方法:

			StaticVar s1 = new StaticVar();
            StaticVar s2 = new StaticVar();
            s1.count();//1
            s1.count();//2
            s1.count();//3
            s2.count();//4
            s2.count();//5
            s2.count(); //6

繼承

已有的類被稱為的基類,新的類被稱為派生類。一個類可以派生自多個類或介面。C# 不支援多重繼承,但是可以使用介面來實現多重繼承,例如:

	//基類
  class Shape 
   {
      public void setWidth(int w)
      {
         width = w;
      }
      public void setHeight(int h)
      {
         height = h;
      }
      protected int width;
      protected int height;
   }

   // 介面
   public interface PaintCost 
   {
      int getCost(int area);

   }
   // 派生類
   class Rectangle : Shape, PaintCost
   {
      public int getArea()
      {
         return (width * height);
      }
      public int getCost(int area)
      {
         return area * 70;
      }
   }

在子類中用 override 重寫父類中用 virtual 申明的虛方法時,例項化父類呼叫該方法,執行時呼叫的是子類中重寫的方法;
例如:

/// <summary>  
/// 父類  
/// </summary>  
public class ParentClass  
{  
   public virtual void ParVirMethod()  
   {  
       Console.WriteLine("父類的方法...");  
   }  
}  

/// <summary>  
/// 子類1  
/// </summary>  
public class ChildClass1 : ParentClass  
{  
   public override void ParVirMethod()  
   {  
       Console.WriteLine("子類1的方法...");  
   }  
} 

如果子類中用 new 覆蓋父類中用 virtual 申明的虛方法時,例項化父類呼叫該方法,執行時呼叫的是父類中的虛方法
例如:

/// <summary>  
/// 父類  
/// </summary>  
public class ParentClass  
{  
   public virtual void ParVirMethod()  
   {  
       Console.WriteLine("父類的方法...");  
   }  
}  
/// <summary>  
/// 子類2  
/// </summary>  
public class ChildClass2 : ParentClass  
{  
   public new void ParVirMethod()  
   {  
       Console.WriteLine("子類2的方法...");  
   }  

   public void Test()  
   {  
       Console.WriteLine("子類2的其他方法...");  
   }  
}  

總結:override是重寫,即將基類的方法在派生類裡直接抹去重新寫,故而呼叫的方法就是子類方法;而new只是將基類的方法在派生類裡隱藏起來,故而呼叫的仍舊是基類方法。

多型性

分為:靜態多型性和動態多型性。

C# 提供了兩種技術來實現靜態多型性。分別為:

1.函式過載:函式的定義不同,引數型別不同,引數個數不同。
2.運算子過載

動態多型性
動態多型性是通過 抽象類 和 虛方法 實現的。
使用關鍵字 abstract 建立抽象類,用於提供介面的部分類的實現。抽象類包含抽象方法,抽象方法可被派生類實現。
注意:

1.不能建立一個抽象類的例項。
2.不能在一個抽象類外部宣告一個抽象方法。
3.通過在類定義前面放置關鍵字 sealed,可以將類宣告為密封類。當一個類被宣告為 sealed 時,它不能被繼承。抽象類不能被宣告為 sealed。

當有一個定義在類中的函式需要在繼承類中實現時,可以使用虛方法virtual。虛方法可以在不同的繼承類中有不同的實現。
例如:為使用者自定義的類 Box 實現了加法運算子(+)。它把兩個 Box 物件的屬性相加,並返回相加後的 Box 物件。

public static Box operator+ (Box b, Box c)
{
   Box box = new Box();
   box.length = b.length + c.length;
   box.breadth = b.breadth + c.breadth;
   box.height = b.height + c.height;
   return box;
}

注意:運算子只能採用值引數,不能採用 ref 或 out 引數。

介面

1.介面定義了屬性、方法和事件,這些都是介面的成員。
2.介面只包含了成員的宣告。
3.通常介面命令以 I 字母開頭。
4.介面方法不能用public abstract等修飾。
5.必須實現介面的所有方法。
下面是一個高頻率的筆試題:

介面和抽象類的區別

1.抽象類是類,所以只能被單繼承,但是介面卻可以一次實現多個
2.介面中只能宣告方法,屬性,事件,索引器。而抽象類中可以有方法的實現,也可以定義非靜態的類變數。
3.抽象類的例項是它的子類給出的。介面的例項是實現介面的類給出的。
4.介面成員是公共的,但抽象類的成員也可以是任意的。

名稱空間namespace

作用:一個名稱空間中宣告的類的名稱與另一個名稱空間中宣告的相同的類的名稱不衝突。
定義:

//名稱空間1
namespace MyNamespace1
{
   // 程式碼宣告
   class MyClass()
   {
	   	public MyFun1()
	   	{
	   	}
   }
}

//名稱空間2
namespace MyNamespace2
{
   // 程式碼宣告
   class MyClass()
   {
	   	public MyFun2()
	   	{
	   	}
   }
}

使用:
方法一:

MyNamespace1.MyClass class1=new MyNamespace1.MyClass();
MyNamespace2.MyClass class2=new MyNamespace2.MyClass();
class1.MyFun1();
class2.MyFun2();

方法二:使用using關鍵字
using 關鍵字表明程式使用的是給定名稱空間中的名稱
例如:

using MyNamespace1;

MyClass  myFun=new MyClass ();
MyClass .MyFun1();

總結using的用法:

  1. using指令:引入名稱空間,例如:
using System;
using Namespace1.SubNameSpace;
  1. using static 指令:指定無需指定型別名稱即可訪問其靜態成員的型別
using static System.Math;var = PI; // 直接使用System.Math.PI
  1. 起別名
using Project = PC.MyCompany.Project;
  1. using語句:將例項與程式碼繫結
using (Font font3 = new Font("Arial", 10.0f),
            font4 = new Font("Arial", 10.0f))
{
    // Use font3 and font4.
}
//程式碼段結束時,自動呼叫font3和font4的Dispose方法

前處理器#define

前處理器指令的作用:由於前處理器指令可以禁止編譯器編譯程式碼的某一部分,所以計劃釋出兩個版本的程式碼,即基本版本和有更多功能的企業版本,就可以使用這些前處理器指令來控制。
1.建立符號常量,使用方法如下:

#define PI 

#if (PI)
    Console.WriteLine("PI is defined");
#else
    Console.WriteLine("PI is not defined");

正則表示式

正則表示式是一種匹配輸入文字的模式。模式由一個或多個字元、運算子和結構組成。
正則表示式可以用來檢查一個串是否含有某種子串、將匹配的子串替換或者從某個串中取出符合某個條件的子串等。
正則表示式中的反斜槓字元(\)指示其後跟的字元是特殊字元。
正則表示式的限定符:
在這裡插入圖片描述
正則表示式的定位符:
在這裡插入圖片描述
注意:不能將限定符與定位符一起使用。
關於正則表示式的學習,請參考:http://www.runoob.com/regexp/regexp-tutorial.html

異常處理

C# 異常處理是建立在四個關鍵詞之上的:try、catch、finally 和 throw。
格式:

try
{
   // 引起異常的語句
}
catch( ExceptionName e1 )
{
   // 錯誤處理程式碼
}
catch( ExceptionName e2 )
{
   // 錯誤處理程式碼
}
catch( ExceptionName eN )
{
   // 錯誤處理程式碼
}
finally
{
   // 要執行的語句
}
Catch(Exception e)
{
   ...
   Throw e
}

C# 中的異常類主要是直接或間接地派生於 System.Exception 類。System.ApplicationException 和 System.SystemException 類是派生於 System.Exception 類的異常類。

檔案的輸入與輸出

當開啟檔案進行讀寫時,它變成一個 流。流包括:輸入流 和 輸出流。輸入流用於從檔案讀取資料(讀操作),輸出流用於向檔案寫入資料(寫操作)。
System.IO 名稱空間有各種不同的類,用於執行各種檔案操作,如建立和刪除檔案、讀取或寫入檔案,關閉檔案等。