您的位置:首页 > 移动开发 > Objective-C

C#学习笔记——进阶基础:多态、内部类及Object类

2020-07-04 02:27 218 查看

C#学习笔记——进阶基础(2.2):多态、内部类及Object类

多态性

面向对象程序设计中的多态性是一个重要的概念。所谓多态性,就是同一签名具有不同的表现行为,运算符重载和函数重载都属于多态性的表现形式。

隐藏基类方法

当派生类从基类继承时,它会获得基类的所有方法、字段、属性和事件。若要更改基类的数据和行为,有两种选择:
方法1:使用新的派生成员替换基成员(示例:在使用新的派生方法替换基方法时应使用new关键字)

class A
{
public void fun()
{Console.WriteLine("A");}
}
class B : A
{
new public void fun() //隐藏基类方法fun
{Console.WriteLine("B");}
}

方法2:重写虚拟的基成员。

重写

重写是指在子类中编写有相同名称和参数的方法。重写和重载的区别:后者是指编写(在同一个类中)具有相同的名称,却有不同的参数的方法。也就是说,重写是指子类中的方法与基类中的方法具有相同的签名,而重载方法具有不同的签名。

  1. virtual关键字
    virtual关键字用于修饰方法、属性、索引器或事件声明,并且允许在派生类中重写这些对象。
    调用虚方法时,首先调用派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。
    注意:默认情况下,方法是非虚拟的,不能重写非虚方法。virtual修饰符不能与static、abstract和override修饰符一起使用。在静态属性上使用virtual修饰符是错误的
    这个规则与OOP课程学习的C++重写规则一致。
  2. 重写方法
    override方法提供从基类继承的成员的新实现。
    通过override声明重写的方法称为重写基方法。
    重写的基方法必须与override方法具有相同的签名。注意:不能重写非虚方法或静态方法。重写的基方法必须是virtual、abstract或override的。
    如下面的代码所示
class Student
{
protected int no;                //学号
protected string name;      //姓名
protected string tname;     //班主任或指导教师
public void setdata(int no1, string name1, string tname1)
{
no = no1; name = name1; tname = tname1;
}
public virtual void dispdata() //虚方法
{
Console.WriteLine("本科生 学号:{0} 姓名:{1} 班 主 任:{2}", no, name, tname);
}
}
class Graduate : Student
{
public override void dispdata() //重写方法
{
Console.WriteLine("研究生  学号:{0} 姓名:{1} 指导教师:{2}", no, name, tname);
}
}
class Program
{
static void Main(string[] args)
{
Student s = new Student();
s.setdata(101, "王华", "李量");
s.dispdata();
Graduate g = new Graduate();
g.setdata(201, "张华", "陈军");
g.dispdata();
}
}

dynamic类型

C#的多态性就像C++的动态联编一样,不是在程序编译时进行静态连接,而是在程序运行时进行动态连接。C#还基于动态性引入了dynamic类型,dynamic类型的变量只有在运行时才能被确定具体类型,而编译器也会绕过对这种类型的语法检查。例如,声明如下类:
在大多数情况下,dynamic 类型与object类型的行为是一样的。只是编译器不会对包含 dynamic 类型表达式的操作进行解析或类型检查。编译器将有关该操作信息打包在一起,并且该信息以后用于运行时的计算操作。也就是说,dynamic 类型仅在编译期间存在,在运行期间它会被 object类型替代。

对象的类型判别和类对象引用的转换

类对象引用的转换

对于具有继承关系的类,可以将派生类对象引用转换为基类对象引用,例如,有以下类声明:

class A                               //声明类A
{
public void funa()
{
Console.WriteLine("A.funa");
}
}

class B : A                          //B类派生自A类
{
public void funb()
{
Console.WriteLine("B.funb");
}
}

下面的转换是正确的:

B b = new B();
A a = b;        //类对象引用的隐式转换
a.funa();

因此,类对象引用转换的规则如下:

  1. 一个基类的对象引用变量可以指向其子类的对象。
  2. 一个基类的对象引用变量不可以访问其子类的对象新增加的成员。

is运算符

is运算符用于检查对象是否为某种类型,或者可以转换为给定的类型。
如果是,这个运算符返回true;否则,返回false。
is运算符不能重载。
is运算符的语法格式如下:
operand is type
如果type是一个类类型,而operand也是该类型,或者它继承了该类型,或者它可以装箱到该类型,则结果为true;
如果type是一个接口类型,而operand也是该类型,或者它是实现该接口的类型,则结果为true;
如果type是一个值类型,而operand也是该类型,或者它可以拆箱到该类型,则结果为true。

as运算符

C#还提供了as运算符,用于在兼容的引用类型之间执行转换。
它类似于强制转换,所不同的是,当转换失败时,运算符将产生空(null),而不是引发异常。
as的语法格式如下:
operand as type
等效于
operand is type ? (type)operand : (type)null
其中,“表达式”只被计算一次。
as运算符仅适合以下情况:
operand的类型是type类型。
operand可以隐式转换为type类型。
operand可以装箱到type中。

嵌套类

嵌套类的声明

在包含类的类声明中,直接添加嵌套类的类声明。
例如,以下声明了包含类A,其中包含嵌套类B:

class A                 //声明包含类A
{
…
private class B   //声明嵌套类B
{       //嵌套类的成员
}
…
}

嵌套类和包含类的关系

嵌套类可访问包含类。若要访问包含类,需将其作为构造函数传递给嵌套类。

object

object是终极基类,所有类和结构都从它派生。object 类型是 System.Object 在 .NET 中的别名。 在 C# 的统一类型系统中,所有类型(预定义类型、用户定义类型、引用类型和值类型)都是直接或间接从 System.Object 继承的。 可以将任何类型的值赋给 object 类型的变量。 可以使用文本 null 将任何 object 变量赋值给其默认值。 将值类型的变量转换为对象的过程称为装箱。 将 object 类型的变量转换为值类型的过程称为取消装箱 。
System.Object类的方法:

Equals(Object) 确定指定的对象是否等于当前对象。
Equals(Object, Object) 确定指定的对象实例是否被视为相等。
Finalize() 在垃圾回收将某一对象回收前允许该对象尝试释放资源并执行其他清理操作。
GetHashCode() 作为默认哈希函数。
GetType() 获取当前实例的 Type。
MemberwiseClone() 创建当前 Object 的浅表副本。
ReferenceEquals(Object, Object) 确定指定的 Object 实例是否是相同的实例。
ToString() 返回表示当前对象的字符串。

重写ToString()

在对象上调用ToString()默认返回类的完全限定名称。
例如,在一个System.IO.FileStream对象上调用ToString()会返回字符串"System.IO.FileStream"。
但ToString()对于某些类应返回更有意义的结果。以string类为例,ToString()应返回字符串值本身。类似地,返回一个Contact的姓名显然更有意义。下面代码清单重写了ToString(),返回Person的字符串表示。

public class Person
{
int id;
string name;
int age;
public Person() { }
public Person(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
}

public override string ToString()
{
return $"id:{id}, name:{name},age:{age}";
}
}

直接输出Person类的对象:

static void Main(string[] args){    Person person = new Person(1,"张三", 18);    Console.WriteLine(person);}//写法一
Console.WriteLine(person.ToString());}//写法二

写法一与写法二调用相同。

规范

要重写ToString()以返回有用的、面向开发人员的诊断宇符串。要使ToString()返回的字符串简短。不要从ToString()返回空字符串代表“空"(null)。避免ToString()引发异常或造成可观察到的副作用(改变对象状态)。
如果返回值与语言文化相关或需要格式化(如DateTime),就要重载ToString(stringformat)或实现IFormattable。考虑从ToString()返回独一无二的字符串以标识对象实例。

索引器

索引器提供了一种访问类或结构的方法,即允许按照与数组相同的方式对类、结构或接口进行索引。它的引入是为了使程序更加直观、易于理解。

声明索引器

要声明类或结构上的索引器,需使用this关键字,其语法格式如下:

public int this[int index]    //索引器声明
{
// get和set访问器
}

其中,this关键字引用类的当前实例。从中看到,索引器像对普通属性一样,为它提供get和set方法,这些访问器指定当使用该索引器时将引用到什么内部成员。

public class University
{
const int MAX = 5;
private string[] name = new string[MAX];
public string this[int index]  //索引器
{
get
{
if (index >= 0 && index < MAX)
return name[index];
else
return name[0];
}

set
{
if (index >= 0 && index < MAX)
name[index] = value;
}
}
}

索引类型

C#并不将索引类型限制为整数。例如,可以对索引器使用字符串。通过搜索集合内的字符串并返回相应的值,可以实现此类的索引器。由于访问器可被重载,字符串和整数版本可以共存。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: