C# 程式設計/類
與其他面向物件程式語言一樣,C# 程式的功能在 類 中實現。類的 方法 和 屬性 包含定義類行為的程式碼。
C# 類透過將功能 封裝 在屬性和方法中,並透過啟用多種型別的 多型(包括透過 繼承 的 子型別多型 和透過 泛型 的 引數多型)來支援 資訊隱藏。
可以定義幾種型別的 C# 類,包括 例項 類(可以例項化的 標準 類)、靜態 類和 結構體。
類使用 class 關鍵字定義,後面跟著一個識別符號來命名類。然後可以使用 new 關鍵字後跟類的名稱來建立類的例項。
下面的程式碼定義了一個名為 Employee 的類,它具有 Name 和 Age 屬性,以及空方法 GetPayCheck() 和 Work()。它還定義了一個 Sample 類,該類例項化並使用 Employee 類
public class Employee
{
private int _Age;
private string _Name;
public int Age
{
get { return _Age; }
set { _Age = value; }
}
public string Name
{
get { return _Name; }
set { _Name = value; }
}
public void GetPayCheck()
{
}
public void Work()
{
}
}
public class Sample
{
public static void Main()
{
Employee marissa = new Employee();
marissa.Work();
marissa.GetPayCheck();
}
}
C# 方法 是包含程式碼的類成員。它們可能具有返回值和 引數 列表,以及 泛型 型別宣告。與欄位一樣,方法可以是 靜態 的(與類相關聯並透過類訪問)或 例項 的(與類的物件例項相關聯並透過該類的物件例項訪問,以及 泛型 型別宣告)。
從 C# 4.0 開始,方法可以具有具有預設值的可選引數,正如 C++ 使用者已經知道的。例如,方法
void Increment(ref int x, int dx = 1)
可以只用一個引數呼叫,因為第二個引數dx被初始化為預設值。[1]
類的 建構函式 控制其初始化。當程式請求類型別的新物件時,建構函式的程式碼執行以初始化類的例項。建構函式通常設定其類的屬性,但它們不限於這樣做。
與其他方法一樣,建構函式可以具有 引數。要使用帶引數的建構函式建立物件,new 命令接受引數。下面的程式碼定義瞭然後例項化了 Employee 類的多個物件,一次使用不帶引數的建構函式,一次使用帶引數的版本
public class Employee
{
public Employee()
{
System.Console.WriteLine("Constructed without parameters");
}
public Employee(string strText)
{
System.Console.WriteLine(strText);
}
}
public class Sample
{
public static void Main()
{
System.Console.WriteLine("Start");
Employee Alfred = new Employee();
Employee Billy = new Employee("Parameter for construction");
System.Console.WriteLine("End");
}
輸出
Start Constructed without parameters Parameter for construction End
建構函式可以相互呼叫
public class Employee
{
public Employee(string strText, int iNumber)
{
...
}
public Employee(string strText)
: this(strText, 1234) // calls the above constructor with user-specified text and the default number
{ }
public Employee()
: this("default text") // calls the above constructor with the default text
{ }
}
終結器 與建構函式相反,它們定義了物件的最終行為,並在物件不再使用時執行。儘管它們經常在 C++ 中用於釋放物件保留的資源,但由於 .NET Framework 垃圾收集器,它們在 C# 中的使用頻率較低。物件的終結器(不接受任何引數)在物件不再被引用後的一段時間內被呼叫,但垃圾收集的複雜性使得終結器的具體時間不確定。
public class Employee
{
public Employee(string strText)
{
System.Console.WriteLine(strText);
}
~Employee()
{
System.Console.WriteLine("Finalized!");
}
public static void Main()
{
Employee marissa = new Employee("Constructed!");
marissa = null;
}
}
輸出
Constructed! Finalized!
C# 屬性 是類成員,它們使用 欄位 的語法公開方法的功能。它們簡化了呼叫傳統 get 和 set 方法(也稱為 訪問器 方法)的語法。與方法一樣,它們可以是 靜態 的或 例項 的。
屬性以以下方式定義
public class MyClass
{
private int m_iField = 3; // Sets integerField with a default value of 3
public int IntegerField
{
get
{
return m_iField; // get returns the field you specify when this property is assigned
}
set
{
m_iField = value; // set assigns the value assigned to the property of the field you specify
}
}
}
對於 getter/setter 方法,更簡短的方式是訪問器,它們在一行中完成兩者
class Culture
{
public int TalkedCountries { get; set; }
public string Language { get; set; }
}
class InterculturalDialogue
{
Culture culture;
culture.Language = "Italian"; // ==> culture.SetLanguage("Italian");
string strThisLanguage = culture.Language; // ==> ... = culture.GetLanguage();
}
該程式碼等效於 GetLanguage 和 SetLanguage 方法定義,但無需定義這些方法。使用者可以直接訪問成員,當然,如果它不是私有的。
C# 關鍵字 value 包含分配給屬性的值。定義屬性後,可以使用它就像一個變數。如果你要在屬性的 get 和 set 部分編寫一些額外的程式碼,它將像一個方法一樣工作,允許你在讀寫變數之前操作資料。
public class MyProgram
{
MyClass myClass = new MyClass;
Console.WriteLine(myClass.IntegerField); // Writes 3 to the command line.
myClass.IntegerField = 7; // Indirectly assigns 7 to the field myClass.m_iField
}
以這種方式使用屬性提供了一種乾淨且易於使用的機制來保護資料。
C# 索引器 是類成員,它們定義 陣列訪問 操作的行為(例如 list[0] 用於訪問 list 的第一個元素,即使 list 不是陣列)。
要建立索引器,請使用 this 關鍵字,如以下示例所示
public string this[string strKey]
{
get { return coll[strKey]; }
set { coll[strKey] = value; }
}
此程式碼將建立一個返回字串值的字串索引器。例如,如果類是 EmployeeCollection,你可以編寫類似以下程式碼
EmployeeCollection e = new EmployeeCollection();
.
.
.
string s = e["Jones"];
e["Smith"] = "xxx";
C# 事件 是類成員,它們向類的客戶端公開通知。事件只觸發,從不賦值。
using System;
// Note: You need to know some about delegate, properties and methods to understand this sample
namespace EventSample
{
/// <summary>
/// This delegate defines the signature of the appropriate method
/// </summary>
public delegate void ContractHandler(Employee sender);
/// <summary>
/// Employee class
/// </summary>
public class Employee
{
/// <summary>
/// Field for the info whether or not the Employee is engaged
/// </summary>
private bool bIsEngaged = false;
/// <summary>
/// Age of the employee
/// </summary>
private int iAge = -1;
/// <summary>
/// Name of the employee
/// </summary>
private String strName = null;
/// <summary>
/// *** The our event ***
/// Is a collection of methods that will be called when it fires
/// </summary>
public event ContractHandler Engaged;
/// <summary>
/// Standard constructor
/// </summary>
public Employee()
{
// Here, we are adding a new method with appropriate signature (defined by delegate)
// note: when an event has no method and it was fired, it causes an exception!
// for all effects when programming with events, assign one private method to event
// or simply do a verification before firing it! --> if (event != null)
this.Engaged += new ContractHandler(this.OnEngaged);
}
/// <summary>
/// Event handler for the "engaged" event
/// </summary>
/// <param name="sender">
/// Sender object
/// </param>
private void OnEngaged(Employee sender)
{
Console.WriteLine("private void OnEngaged was called! this employee is engaged now!");
}
/// <summary>
/// Accessor for the employee name
/// </summary>
public string Name
{
get
{
return strName;
}
set
{
strName = value;
}
}
/// <summary>
/// Accessor for the employee age
/// </summary>
public int Age
{
get
{
return m_iAge;
}
set
{
m_iAge = value;
}
}
/// <summary>
/// Accessor for the information about employee engagement
/// </summary>
public bool IsEngaged
{
get
{
return bIsEngaged;
}
set
{
if (bIsEngaged == false && value == true)
{
// here we fires event (call all the methods that it have)
// all times when IsEngaged is false and set to true;
Engaged(this);
}
bIsEngaged = value;
}
}
}
/// <summary>
/// Class for the entry point
/// </summary>
public class EntryPointClass
{
static void Main(string[] a_strArgs)
{
Employee simpleEmployee = new Employee();
simpleEmployee.Age = 18;
simpleEmployee.Name = "Samanta Rock";
// Here...
// This is saying when the event fires, the method added to event are called too.
// note that we cannot use =
// is only += to add methods to event or -= do retire an event
simpleEmployee.Engaged += new ContractHandler(SimpleEmployee_Engaged);
// make attention here...
// when I assign true to this property,
// the event Engaged will be called
// when event is called, all method that it have, are called!
simpleEmployee.IsEngaged = true;
Console.ReadLine();
return;
}
/// <summary>
/// Event handler for the registered "engaged" event
/// </summary>
/// <param name="sender">
/// Event sender
/// </param>
static void SimpleEmployee_Engaged(Employee sender)
{
Console.WriteLine("The employee {0} is happy!", sender.Name);
}
}
}
有關詳細資訊,請參閱 此處。
C# operator 定義是類成員,它們定義或重新定義類例項上基本 C# 運算子(隱式或顯式呼叫)的行為
public class Complex
{
private double m_dReal, m_dImaginary;
public double Real
{
get { return m_dReal; }
set { m_dReal = value; }
}
public double Imaginary
{
get { return m_dImaginary; }
set { m_dImaginary = value; }
}
// binary operator overloading
public static Complex operator +(Complex c1, Complex c2)
{
return new Complex() { Real = c1.Real + c2.Real, Imaginary = c1.Imaginary + c2.Imaginary };
}
// unary operator overloading
public static Complex operator -(Complex c)
{
return new Complex() { Real = -c.Real, Imaginary = -c.Imaginary };
}
// cast operator overloading (both implicit and explicit)
public static implicit operator double(Complex c)
{
// return the modulus - sqrt(x^2 + y^2)
return Math.Sqrt(Math.Pow(c.Real, 2) + Math.Pow(c.Imaginary, 2));
}
public static explicit operator string(Complex c)
{
// we should be overloading the ToString() method, but this is just a demonstration
return c.Real.ToString() + " + " + c.Imaginary.ToString() + "i";
}
}
public class StaticDemo
{
public static void Main()
{
Complex number1 = new Complex() { Real = 1, Imaginary = 2 };
Complex number2 = new Complex() { Real = 4, Imaginary = 10 };
Complex number3 = number1 + number2; // number3 now has Real = 5, Imaginary = 12
number3 = -number3; // number3 now has Real = -5, Imaginary = -12
double testNumber = number3; // testNumber will be set to the absolute value of number3
Console.WriteLine((string)number3); // This will print "-5 + -12i".
// The cast to string was needed because that was an explicit cast operator.
}
}
結構體(或 structs)使用 struct 關鍵字定義,後面跟著一個 識別符號 來命名結構體。它們與類類似,但有一些細微的差別。結構體 用作類的輕量級版本,可以幫助在處理小型資料結構時減少記憶體管理工作。然而,在大多數情況下,使用標準 類 是更好的選擇。
struct 和 class 的主要區別在於 struct 的 例項 是 值,而 class 的 例項 是 引用。 因此,當您按值將 struct 傳遞給函式時,您會獲得物件的副本,因此對它的更改不會反映在原始物件中,因為現在有兩個不同的物件。 但如果您按引用傳遞 class 的例項,那麼只有一個例項。
下面的 Employee 結構聲明瞭一個 public 欄位和一個 private 欄位。 透過 public 屬性 Name 可以訪問 private 欄位。
struct Employee
{
public int m_iAge;
private string m_strName;
public string Name
{
get { return m_strName; }
set { m_strName = value; }
}
}
從 C# 2.0 開始,可以在結構中包含 陣列,但只能在不安全的上下文中。
struct data
{
int header;
fixed int values[10];
}
使用指標運算訪問陣列。 值將以陣列值作為 C 風格陣列使用索引等方式進行處理。
結構建構函式
[edit | edit source]結構需要建構函式 - 或者更準確地說,是初始化器,因為它們不是構造而是僅僅初始化記憶體 [2] - 以便其內容不會未初始化。 因此,不允許沒有引數的建構函式。
當且僅當賦值右側的結構變數都已初始化時,結構變數才能彼此賦值。 [3]
struct Timestamp
{
private ushort m_usYear;
private ushort m_usMonth;
private ushort m_usDayOfMonth;
private ushort m_usHour;
private ushort m_usMinute;
private ushort m_usSecond;
public Timestamp(ushort usYear,
ushort usMonth,
ushort usDay,
ushort usHour,
ushort usMinute,
ushort usSecond)
{
m_usYear = usYear - 1900;
m_usMonth = usMonth;
m_usDay = usDay;
m_usHour = usHour;
m_usMinute = usMinute;
m_usSecond = usSecond;
}
}
靜態類
[edit | edit source]靜態類 通常用於實現 單例模式。 static 類的所有方法、屬性和欄位也都是 static(例如 System.Console 類的 WriteLine() 方法),因此可以在不例項化 static 類的情況下使用它們。
public static class Writer
{
public static void Write()
{
System.Console.WriteLine("Text");
}
}
public class Sample
{
public static void Main()
{
Writer.Write();
}
}
參考文獻
[edit | edit source]- ↑ [[[w:C_Sharp_syntax#Optional_parameters|Optional parameters]] "Optional parameters"]. Retrieved 2018-02-01.
{{cite web}}: Check|url=value (help) - ↑ Greg Beech (2008-06-16). "Structure constructors". http://social.msdn.microsoft.com/: MSDN. Retrieved 2012-04-12.
因為結構只是記憶體中的一個內聯區域,所以它們不能為 null,所以 CLR 必須能夠確保該記憶體區域完全初始化,而不是部分垃圾。 出於這個原因,您經常會聽到結構的“建構函式”被稱為(可能更準確地說)“初始化器”,因為它們不是構造物件而是僅僅初始化記憶體區域。
{{cite web}}: External link in(help)|location= - ↑ John Sharp. "Microsoft® Visual C#® 2005 Step by Step / Copying Structure Variables". http://books.google.at/: Google Books. Retrieved 2012-04-12.
您可以初始化或將一個 struct 變數賦值給另一個 struct 變數,但前提是右側的 struct 變數必須完全初始化(即,如果其所有欄位都已初始化)。
{{cite web}}: External link in(help)|location=