跳转至内容

C# 编程/类

来自维基教科书,开放的书籍,为一个开放的世界

与其他面向对象编程语言一样,C# 程序的功能在 中实现。类的 方法属性 包含定义类行为的代码。

C# 类通过将功能 封装 在属性和方法中,并通过启用多种类型的 多态(包括通过 继承子类型多态 和通过 泛型参数多态)来支持 信息隐藏

可以定义几种类型的 C# 类,包括 实例 类(可以实例化的 标准 类)、静态 类和 结构体

类使用 class 关键字定义,后面跟着一个标识符来命名类。然后可以使用 new 关键字后跟类的名称来创建类的实例。

下面的代码定义了一个名为 Employee 的类,它具有 NameAge 属性,以及空方法 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# 属性 是类成员,它们使用 字段 的语法公开方法的功能。它们简化了调用传统 getset 方法(也称为 访问器 方法)的语法。与方法一样,它们可以是 静态 的或 实例 的。

属性以以下方式定义

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 关键字定义,后面跟着一个 标识符 来命名结构体。它们与类类似,但有一些细微的差别。结构体 用作类的轻量级版本,可以帮助在处理小型数据结构时减少内存管理工作。然而,在大多数情况下,使用标准 是更好的选择。

structclass 的主要区别在于 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]
  1. [[[w:C_Sharp_syntax#Optional_parameters|Optional parameters]] "Optional parameters"]. Retrieved 2018-02-01. {{cite web}}: Check |url= value (help)
  2. Greg Beech (2008-06-16). "Structure constructors". http://social.msdn.microsoft.com/: MSDN. Retrieved 2012-04-12. 因为结构只是内存中的一个内联区域,所以它们不能为 null,所以 CLR 必须能够确保该内存区域完全初始化,而不是部分垃圾。 出于这个原因,您经常会听到结构的“构造函数”被称为(可能更准确地说)“初始化器”,因为它们不是构造对象而是仅仅初始化内存区域。 {{cite web}}: External link in |location= (help)
  3. 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 |location= (help)

华夏公益教科书