跳转至内容

C# 编程/对象

来自维基教科书,开放世界中的开放书籍

.NET 框架包含多种语言,这些语言都遵循面向对象编程 (OOP) 的软件开发方法。该标准定义了所有对象都支持

  • 继承 - 继承和扩展现有功能的能力。
  • 封装 - 允许用户仅看到特定部分,并以特定方式与之交互的能力。
  • 多态性 - 对象可以动态分配,但对可以对对象执行的操作有一些可预测性的能力。

对象与现实世界中的对象是同义词。想想任何物体,想想它是什么样子的,它如何测量和交互。在创建 OOP 语言时,推理是,如果它模拟人类的思维过程,它将简化编码体验。

例如,让我们考虑一把椅子,它的尺寸、重量、颜色和它是由什么制成的。在 .NET 中,这些值称为“属性”。这些是定义对象状态的值。请注意,有两种方法可以公开值:字段和属性。建议的方法是公开属性而不是字段。

因此,我们对对象的概念有了一个现实世界的想法。在计算机传递信息方面的实用性方面,在程序中传递对象会消耗大量资源。想想一辆汽车,它有多少属性——数百个、数千个。计算机一直传递这些信息会浪费内存、处理时间,因此使用它是一个坏主意。因此,对象有两种形式

  • 引用类型
  • 值类型

引用类型和值类型

[编辑 | 编辑源代码]

引用类型就像指向值的指针。把它想象成一张写着街道地址的纸,地址通向你的房子——你的有数百个属性的对象。如果你想找到它,去地址说的地方!这正是计算机内部发生的情况。引用存储为一个数字,对应于对象存在的内存中的某个地方。因此,您不必四处移动对象——就像每次想要查看它时都建造一座复制品一样——您只需查看原始对象。

值类型是确切的值本身。值非常适合存储少量信息:数字、日期等。

它们在处理方式上有所不同,因此我们将稍后在本文中进行介绍。

除了查询值之外,我们还需要一种与对象交互的方式,以便可以执行某些操作。想想文件——了解文件的长度很好,但是如何进行 Read() 操作呢?因此,我们可以使用称为*方法*的东西来对对象执行操作。

一个例子是矩形。矩形的属性是

  • 长度
  • 宽度

“函数”(或 .NET 中的*方法*)将是

  • Area (= Length*Width)
  • Perimeter (= 2*Length + 2*Width)

方法不同于属性,因为它们需要对数据进行一些转换才能获得结果。方法可以返回结果(例如 Area),也可以不返回结果。就像上面的椅子一样,如果你 Sit() 在椅子上,没有预期的反应,椅子只是……起作用了!

System.Object

[编辑 | 编辑源代码]

为了支持 OOP 的第一个规则——继承,我们定义了所有对象都将从中派生的东西——这就是 System.Object,也称为 Objectobject。此对象定义了一些所有对象都可以使用的方法(如果需要)。这些方法包括

  • GetHashCode() - 获取该对象唯一的数字。
  • GetType() - 获取有关对象的信息,例如方法名称、对象名称等。
  • ToString() - 将对象转换为文本表示形式 - 通常用于输出到屏幕或文件。

由于所有对象都从此类派生(无论您是否定义它),因此任何类都将具有这三种方法,可以随时使用。由于我们总是从 System.Object 或本身从 System.Object 派生的类继承,因此我们增强和/或扩展了它的功能。就像在现实世界中,人类、猫、狗、鸟、鱼都是“生物”的改进和专门版本。

对象基础

[编辑 | 编辑源代码]

默认情况下,所有对象都是引用类型。为了支持值类型,对象必须从 System.ValueType 抽象类而不是 System.Object 继承。

构造函数

[编辑 | 编辑源代码]

当创建对象时,它们会通过“构造函数”进行初始化。构造函数设置对象,准备使用。因为对象需要在使用之前创建,所以构造函数是隐式创建的,除非开发人员对其进行了不同的定义。构造函数有 3 种类型

  • 复制构造函数
  • 静态构造函数
  • 默认构造函数 - 不接受参数。
  • 重载构造函数 - 接受参数。

重载构造函数会自动删除隐式默认构造函数,因此开发人员必须显式定义默认构造函数,如果他们想使用它。

构造函数是 C# 中的一种特殊方法,允许对象在创建时初始化自身。如果使用构造函数方法,则无需编写单独的方法来将初始值分配给对象的成员变量。

构造函数方法的重要特征

  1. 构造函数方法与类本身的名称相同。
  2. 构造函数方法通常声明为 public。
  3. 构造函数方法声明为 public,因为它用于从声明它的类外部创建对象。我们也可以将构造函数方法声明为 private,但这样构造函数就无法用于创建对象。
  4. 构造函数方法没有返回类型(甚至没有 void)。
  5. C# 为每个类提供一个默认构造函数。此默认构造函数将成员变量初始化为零。但是,如果我们编写自己的构造函数方法,则不会使用默认构造函数。
  6. 构造函数方法用于将初始值分配给成员变量。
  7. 当创建对象时,构造函数由 new 关键字调用。
  8. 我们可以在一个类中定义多个构造函数。这被称为构造函数重载。所有构造函数方法的名称相同,但它们的签名不同,即参数的数量和类型不同。
  9. 如果声明了构造函数,则不会生成默认构造函数。

复制构造函数

[编辑 | 编辑源代码]

复制构造函数通过复制另一个对象中的变量来创建对象。复制构造函数是通过创建所需类型的对象并传递要复制的对象来调用的。

在以下示例中,我们将一个 Rectangle 对象传递给 Rectangle 构造函数,以便新对象与旧对象具有相同的值。

using System;

namespace CopyConstructor
{
    class Rectangle
    {
        public int length;
        public int breadth;

        public Rectangle(int x, int y)         // constructor fn
        {
            length = x;
            breadth = y;
        }

        public Rectangle(Rectangle r)
        {
            length = r.length;
            breadth = r.breadth;
        }

        public void display()
        {
            Console.WriteLine("Length = " + length);
            Console.WriteLine("Breadth = " + breadth);
        }
    }   // end of class Rectangle

    class Program
    {
        public static void Main()
        {
            Rectangle r1 = new Rectangle(5, 10);
            Console.WriteLine("Values of first object");
            r1.display();

            Rectangle r2 = new Rectangle(r1);
            Console.WriteLine("Values of second object");
            r2.display();

            Console.ReadLine();
        }
    }
}

静态构造函数

[编辑 | 编辑源代码]

静态构造函数在运行时首次访问类时首次调用。静态变量始终可访问,因此运行时必须在其首次访问时对其进行初始化。下面的示例在调试器中逐步执行时,将显示 static MyClass() 仅在访问 MyClass.Number 变量时访问。

C# 支持两种类型的构造函数:静态构造函数和实例构造函数。实例构造函数在每次创建该类的对象时都会被调用,而静态构造函数仅被调用一次。静态构造函数在创建类的任何对象之前被调用,通常用于初始化类的任何静态数据成员。

静态构造函数是通过在构造函数定义中使用关键字 static 来声明的。此构造函数不能有任何参数或访问修饰符。此外,类只能有一个静态构造函数。例如

using System;
using System.Collections.Generic;
using System.Text;

namespace StaticConstructors
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            int j = 0;
            Console.WriteLine("Static Number = " + MyClass.Number);
        }
    }

    class MyClass
    {
        private static int _number;
        public static int Number { get { return _number; } }
        static MyClass()
        {
            Random r = new Random();
            _number = r.Next();
        }
    }
}

默认构造函数

[编辑 | 编辑源代码]

默认构造函数不接受任何参数,并且如果不存在其他构造函数,则隐式定义。下面的代码示例显示了创建类之前的结果和之后的結果。

// Created by the developer
class MyClass
{
}

// Created by the compiler
class MyClass : System.Object
{
     public MyClass() : base()
     {
     }
}

重载构造函数

[编辑 | 编辑源代码]

为了以各种形式初始化对象,构造函数允许通过传递参数来定制对象。

 class MyClass
    {
        private int _number;
        public int Number { get { return _number; } }
        
        public MyClass()
        {
            Random randomNumber = new Random();
            _number = randomNumber.Next();
        }
  
        public MyClass(int seed)
        {
            Random randomNumber = new Random(seed);
            _number = randomNumber.Next();
        }
   }

调用其他构造函数

[编辑 | 编辑源代码]

为了最小化代码,如果另一个构造函数实现了更好的功能,您可以指示构造函数调用重载(或默认)构造函数并使用特定参数。

 class MyClass
    {
        private int _number;
        public int Number { get { return _number; } }
        
        public MyClass() : 
             this ( DateTime.Now.Milliseconds ) // Call the other constructor passing in a value.
        {
            
        }
  
        public MyClass(int seed)
        {
            Random r = new Random(seed);
            _number = r.Next();
        }
   }

也可以调用基类的构造函数,而不是当前实例中的构造函数。

 class MyException : Exception
    {
        private int _number;
        public int Number { get { return _number; } }
        
        public MyException ( int errorNumber, string message, Exception innerException)
                 : base( message, innerException )
        {
             _number = errorNumber;
        }
   }

析构函数

[编辑 | 编辑源代码]

除了“构造”之外,对象还可以通过垃圾收集器清除时执行清理操作。与构造函数一样,析构函数也使用与类相同的名称,但前面带有波浪号 (~) 符号。但是,垃圾收集器仅在直接调用或有理由回收内存时才运行,因此析构函数可能很长时间没有机会清理资源。在这种情况下,请查看 Dispose() 方法(来自 IDisposable 接口)的使用。

析构函数通过在构造函数前面使用 ~ 符号来识别,该构造函数没有访问修饰符。例如

 class MyException : Exception
    {
        private int _number;
        public int Number { get { return _number; } }
        
        public MyException ( int errorNumber, string message, Exception innerException)
                : base( message, innerException )
        {
             _number = errorNumber;
        }

        ~MyException()
        {
        }
   }


华夏公益教科书