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=