其他路徑:
CSDN: https://blog.csdn.net/wodehao0808
微信公衆號:程序喵星人
更多資源和視頻教程,QQ:1902686547
2. 面向對象編程
面向對象編程也叫做OOP編程。
簡單來說面向對象編程就是結構化編程,對程序中的變量結構劃分,讓編程更清晰。
2.1 類和對象
2.1.1 類
類實際上是創建對象的模板,每個對象都包含數據集合,並提供了處理和訪問數據的方法。
類定義了類的每個對象(稱爲實例)可以包含什麼數據和功能。
類中的數據和函數稱爲類的成員。
數據成員
函數成員
數據成員:
數據成員是包含類的數據--字段,常量和事件的成員。
函數成員:
函數成員提供了操作類中數據的某些功能。(方法,屬性,構造方法和終結器(析構方法),運算符,和索引器)。
字段的聲明
訪問修飾符 類型 字段名稱;
方法的聲明
訪問修飾符 返回值類型 方法名稱(參數)
{
//方法體
}
2.1.2 對象
類創建的變量叫做對象。
實例化一個對象:
ClassName myClass = new ClassName();
其中ClassName是我們定義的類的名字,myClass是我們聲明的變量(對象)的名字,後面的new是一個關鍵字,使用new 加上類型名()表示對該對象進行構造,如果不進行構造的話,這個對象是無法使用的。
2.1.3 Example: 類和對象
2.1.3.1 Customer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lesson_2_1
{
// 定義一個新的類型(類):Customer
class Customer
{
// 字段,數據成員
public string name;
public string address;
public int age;
public string buyTime;
// 方法,函數成員
public void Show()
{
Console.WriteLine("名字:" + name);
Console.WriteLine("年齡:" + age);
Console.WriteLine("地址:" + address);
Console.WriteLine("購買時間:" + buyTime);
}
}
}
2.1.3.2 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 類和對象
namespace Lesson_2_1
{
class Program
{
static void Main(string[] args)
{
// 如果要使用一個類的話,要先引入它所在的命名空間;
// 因爲Customer位於當前的命名空間下,所以不需要引入,就可以直接使用Customer類;
Customer ctm; // 使用 Customer 模板,聲明瞭一個變量(對象)
ctm = new Customer(); // 對象初始化
ctm.name = "張三";
Console.WriteLine("ctm對象的name = " + ctm.name);
ctm.Show(); // 調用對象的方法
Console.ReadKey();
}
}
}
2.2 類的定義和聲明
定義一個車輛(Vehicle)類,具有Run、Stop等方法,具有Speed(速度)、MaxSpeed(最大速度)、Weight(重量)等域(也叫做字段)。
定義一個向量(Vector3)類,裏面有x,y,z三個字段,有取得長度的方法,有設置屬性(Set)的方法。
2.2.1 Example: 類的定義和聲明
2.2.1.1 Vehicle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lesson_2_2
{
class Vehicle
{
public float speed;
public float maxSpeed;
public float weight;
public void Run()
{
Console.WriteLine("這輛車正在以 {0}m/s 的速度行駛", speed);
}
public void Stop()
{
speed = 0; // 車子停下來
Console.WriteLine("這輛車已經停止了,當前速度是:" + speed);
}
}
}
2.2.1.2 Vector3.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lesson_2_2
{
class Vector3
{
public float x, y, z;
public float GetLength()
{
return (float)Math.Sqrt(x * x + y * y + z * z);
}
}
}
2.2.1.3 Vector3_2.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lesson_2_2
{
class Vector3_2
{
// 一般情況下,都是將字段設爲 private 權限
// private: 可以在類內部訪問,但不可以在外部通過對象直接訪問
private float _fX, _fY, _fZ;
// 通過提供公有方法去改變私有字段的值
public void SetX(float p_fX)
{
_fX = p_fX;
}
public void SetY(float p_fY)
{
_fY = p_fY;
}
public void SetZ(float p_fZ)
{
_fZ = p_fZ;
}
public float GetLength()
{
return (float)Math.Sqrt(_fX * _fX + _fY * _fY + _fZ * _fZ);
}
}
}
2.2.1.4 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 類的定義和聲明
namespace Lesson_2_2
{
class Program
{
static void Main(string[] args)
{
// 車輛
Vehicle vh = new Vehicle();
vh.speed = 60;
vh.Run();
vh.Stop();
// 向量Vector3
Vector3 vec = new Vector3();
vec.x = 1;
vec.y = 1;
vec.z = 1;
Console.WriteLine("向量Vector3的長度 = " + vec.GetLength());
// 向量Vector3_2
Vector3_2 vec2 = new Vector3_2();
// vec2._fX = 1; // private字段,不能在類外部通過對象直接訪問
// vec2._fY = 1;
// vec2._fZ = 1;
vec2.SetX(1);
vec2.SetY(1);
vec2.SetZ(1);
Console.WriteLine("向量Vector3_2的長度 = " + vec2.GetLength());
Console.ReadKey();
}
}
}
2.3 構造函數
構造函數就是用於初始化數據的函數。
聲明基本的構造函數的語法就是聲明一個和所在類同名的方法,但是該方法沒有返回類型。
public class MyClass
{
public MyClass() // 構造函數
{
這個構造函數的函數體
}
}
當我們使用new關鍵字創建類的時候,就會調用構造方法。
我們一般會使用構造方法進行初始化數據的一些操作。
構造函數可以進行重載,跟普通函數重載是一樣的規則。
注意:
當我們不寫,任何構造函數的時候,編譯器會提供給我們一個默認的 無參的構造函數,但是如果我們定義了一個或者多個構造函數,編譯器就不會再提供默認的構造函數。
2.3.1 Example:構造函數
2.3.1.1 Vector3.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lesson_2_3
{
class Vector3
{
private float _fX, _fY, _fZ;
// 當我們聲明一個構造函數後,編譯器不會再爲我們提供一個默認的構造函數了
public Vector3()
{
Console.WriteLine("這是一個無參的構造函數");
}
// 構造函數重載
public Vector3(float p_fX, float p_fY, float p_fZ)
{
Console.WriteLine("這是一個有參的構造函數");
this._fX = p_fX; // 也可以 _fX = p_fX;
this._fY = p_fY; // 也可以 _fY = p_fY;
this._fZ = p_fZ; // 也可以 _fZ = p_fZ;
// this 就是調用該方法的對象
}
public float GetLength()
{
// this 就是調用該方法的對象
return (float)Math.Sqrt(this._fX * this._fX + this._fY * this._fY + this._fZ * this._fZ);
}
public void SetX(float p_fX)
{
this._fX = p_fX;
}
public void SetY(float p_fY)
{
this._fY = p_fY;
}
public void SetZ(float p_fZ)
{
this._fZ = p_fZ;
}
}
}
2.3.1.2 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 構造函數
namespace Lesson_2_3
{
class Program
{
static void Main(string[] args)
{
// 使用無參構造函數
Vector3 vec = new Vector3();
vec.SetX(1);
vec.SetY(1);
vec.SetZ(1);
Console.WriteLine("無參構造函數,向量長度 = " + vec.GetLength());
// 有參構造函數
Vector3 vec2 = new Vector3(1, 1, 1); // 可以發現,使用有參構造函數,可以對字段進行初始化;構造函數的主要作用,也是用於初始化數據;
Console.WriteLine("有參構造函數,向量長度 = " + vec2.GetLength());
Console.ReadKey();
}
}
}
2.4 屬性的定義
2.4.1 屬性的定義
屬性的定義結構:
public int MyIntProp{
get{
// get code
}
set{
//set code
}
}
1,定義屬性需要名字和類型
2,屬性包含兩個塊 get塊和set塊
3,訪問屬性和訪問字段一樣。當取得屬性的值的時候,就會調用屬性中的get塊,所以get塊,需要一個返回值,返回值的類型就是屬性的類型;當我們去給屬性設置值的時候,就會調用屬性中的set塊,我們可以在set塊中通過value訪問到我們設置的值。
2.4.2 只讀或只寫屬性
private string name;
public string name{
get{
return name;
}
}
屬性可以只提供一個set塊或者get塊。
只讀:只提供 get 塊。
只寫:只提供 set 塊。
2.4.3 屬性的訪問修飾符
public string name{
get{
return name;
}
// private 修改了屬性的訪問權限
private set{
name = value;
}
}
2.4.4 自動實現的屬性
public int Age{get;set;}
編譯器會自動創建private int age字段。
2.4.5 Example:屬性的定義
2.4.5.1 Vector3.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lesson_2_4
{
class Vector3
{
// C#一般把字段定義爲private,然後通過屬性訪問和修改字段值
private float _fX, _fY, _fZ;
// 屬性
public float X // 屬性和函數,一般都是使用大寫字母開頭
{
get { return _fX; } // get塊中的返回值類型,需要跟屬性類型保持一致
set { _fX = value; } // set塊中,默認變量value,訪問我們設置的值
}
public float Y
{
get { return _fY; }
set { _fY = value; }
}
public float Z
{
get { return _fZ; }
set { _fZ = value > 0 ? value : 0; } // 增加修改保護,例如銀行餘額不能負數等
}
public Vector3()
{
}
public Vector3(float p_fX, float p_fY, float p_fZ)
{
X = p_fX; // 使用屬性的set塊,修改對應字段的值
Y = p_fY;
Z = p_fZ;
}
public float GetLength()
{
return (float)Math.Sqrt(X * X + Y * Y + Z * Z); // 使用屬性的get塊,獲得對應的值
}
// 只讀屬性
private int _iNum = 10;
public int Num
{
get { return _iNum; }
// 沒有set塊,表示這個屬性是隻讀的
}
// 只寫屬性
private int _iCount;
public int Count
{
set { _iCount = value; Console.WriteLine("Count寫入的值是:" + value); }
// 沒有get塊,表示這個屬性是隻寫的
}
// 屬性的訪問修飾符
private string _strName;
public string Name
{
get { return _strName; }
private set { _strName = value; } // private 修飾符,表示set塊是隻能在類內部調用,外部無效
}
public void SetName(string p_strName)
{
Name = p_strName; // 類內部,可以使用屬性Name的set塊
}
// 自動實現的屬性
public string School { get; set; } // 編譯器,會自動爲 屬性School 生成相對應的字段 private string school;
}
}
2.4.5.2 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 屬性
namespace Lesson_2_4
{
class Program
{
static void Main(string[] args)
{
Vector3 vec = new Vector3(1, 1, 1);
Console.WriteLine("vec的x是:" + vec.X);
Console.WriteLine("vec的y是:" + vec.Y);
Console.WriteLine("vec的z是:" + vec.Z);
Console.WriteLine("vec的長度是:" + vec.GetLength());
// 只讀
// vec.Num = 10; // 無法通過屬性設置值,因爲屬性是隻讀的,沒有set塊
Console.WriteLine("vec的只讀屬性:" + vec.Num);
// 只寫
// int i = vec.Count; // 無法通過屬性讀取屬性值,因爲屬性是隻寫的,沒有get塊
vec.Count = 100;
// 屬性的訪問權限
// vec.Name = "張三"; // 無法通過屬性設置值,因爲屬性的 set塊 的權限是private,外部無法直接訪問
vec.SetName("張三"); // 通過額外的函數,在函數中(類內部)去訪問該屬性的 set塊(private權限)
Console.WriteLine("vec的name是:" + vec.Name);
// 自動實現的屬性
vec.School = "中學";
Console.WriteLine("vec的shcool是:" + vec.School);
Console.ReadKey();
}
}
}
2.5 匿名類型
我們創建變量(對象的時候),必須指定類型,其實我們也可以不去指定類型,這個就是匿名類型,我們可以使用var聲明一個匿名類型。
使用var聲明的匿名類型,當初始化的時候,這個變量的類型就被確定下來,並且以後不可以修改。
var var1 = 34;
2.5.1 Example:匿名類型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 匿名類型
namespace Lesson_2_5
{
class Program
{
static void Main(string[] args)
{
// 使用 var 聲明匿名類型
var v1 = 12; // 根據初始化的值,推導出v1的類型;類型確定後,就不能更改
//v1 = "張三"; // v1的類型,已經推導確定了,不能再更改
var pi = 3.14;
var name = "張三";
Console.WriteLine("請輸入:");
var s = Console.ReadLine();
Console.WriteLine("v1 = " + v1);
Console.WriteLine("pi = " + pi);
Console.WriteLine("name = " + name);
Console.WriteLine("s = " + s);
Console.ReadKey();
}
}
}
2.6 堆和棧
堆和棧 : 程序運行時的內存區域。
我們把內存分爲 堆空間 和 棧空間。
棧空間 比較小,但是讀取速度快。
堆空間 比較大,但是讀取速度慢。
2.6.1 棧
棧的特徵:
數據只能從棧的頂端插入和刪除。
把數據放入棧頂稱爲入棧(push)。
從棧頂刪除數據稱爲出棧(pop)。
2.6.2 堆
堆是一塊內存區域,與棧不同,堆裏的內存能夠以任意順序存入和移除。
2.6.3 GC垃圾回收
GC Garbage Collector垃圾回收器。
CLR的GC就是內存管理機制,我們寫程序不需要關心內存的使用,因爲這些都是CLR幫我們做了。
2.6.4 值類型和引用類型
類型被分爲兩種:值類型(整數,bool struct char 小數等) 和 引用類型(string 數組 自定義的類,內置的類等)。
值類型只需要一段單獨的內存,用於存儲實際的數據,(單獨定義的時候放在棧中)。
引用類型需要兩段內存:
第一段存儲實際的數據,它總是位於堆中。
第二段是一個引用,指向數據在堆中的存放位置。
當我們使用引用類型賦值的時候,其實是賦值給引用類型的引用(即在堆中的地址)。
如果數組是一個值類型的數組,那麼數組中直接存儲值。如果是一個引用類型的數組(數組中存儲的是引用類型),那麼數組中存儲的是引用(內存地址)。
2.6.4.1 Example:值類型和引用類型
2.6.4.1.1 Vector3.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Lesson_2_6
{
class Vector3
{
public float x, y, z;
}
}
2.6.4.1.2 Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 值類型和引用類型
namespace Lesson_2_6
{
class Program
{
static void Main(string[] args)
{
Test1();
Test2();
Test3();
Test4();
Test5();
Console.ReadKey();
}
static void Test1()
{
int i = 34; // 值類型,在棧中存儲值:34
int j = 34; // 值類型,在棧中存儲值:34
int temp = 34; // 值類型,在棧中存儲值:34
char c = 'a'; // 值類型,在棧中存儲值:a
bool b = true; // 值類型,在棧中存儲值:true110
}
static void Test2()
{
int i = 34; // 值類型,在棧中存儲值:34
int j = 234; // 值類型,在棧中存儲值:34
string name = "my"; // 引用類型,在堆中存儲值:my;在棧中存儲(即name的值):my在堆中的地址。
}
static void Test3()
{
string name = "my";
string name2 = "you";
name = name2; // name保存:you在堆中的地址
name = "baidu"; // name保存:baidu在堆中的地址
Console.WriteLine("name = {0}, name2 = {1}", name, name2);
}
static void Test4()
{
Vector3 v = new Vector3(); // v指向堆1
v.x = 100;
v.y = 100;
v.z = 100;
Vector3 v2 = new Vector3(); // v2指向堆2
v2.x = 200;
v2.y = 200;
v2.z = 200;
v2 = v; // v2指向堆1
v2.x = 300; // 將堆1中的變量x的值改爲300
Console.WriteLine("v.x = " + v.x); // v也是指向堆1的,所以,輸出的值是 300
}
static void Test5()
{
// 如果數組是一個值類型的數組,那麼數組中直接存儲值;
// 如果數組是一個引用類型的數組(數組中存儲的是引用類型),MAME數組中存儲的是引用(即內存地址)
Vector3[] vArray = new Vector3[] { new Vector3(), new Vector3(), new Vector3() };
Vector3 v1 = vArray[0]; // v1指向堆1
vArray[0].x = 100; // 修改堆1中的x的值
v1.x = 200; // 修改堆1中的x的值
Console.WriteLine("vArray[0].x = " + vArray[0].x); // vArray[0]是指向堆1的,所以輸出值是200
}
}
}