目录
C#中的类定义
class MyClass
{
//Members
}
● 这样定义一个类后,就可以在项目中能访问该定义的其他位置对该类进行实例化。 在默认情况下, 类是内部的(internal)—— 即只有当前工程中的代码能够访问。 可以使用关键字 internal 写在 class 的前面 来显式指定这一点。
● 还可以使用关键字public 指定类是公共的—— 即可以在任何项目工程中的代码来访问。
● 还可以使用关键字abstract 指定类是抽象类(该类只能被继承,不能被实例化,可以有抽象成员),抽象类 也可以是 internal 和 public 的 可访问性。
● 还可以使用关键字 sealed 指定类是 密封类(该类不能被继承,就是不能做基类)密封类 也可以是 internal 类 和 public 类 的 可访问性。
class MyClass : base
{
//Members
}
上述代码在类定义中指定继承,表示该类被描述为直接继承自该基类。
● 注意: 除了特殊的类 object, 所有的类都是派生类, 即使它们没有 显式指定继承的基类说明。 类object 是唯一的非派生类, 因为它是继承层次结构的基础。 所以说,如果没有使用基类,被定义的类只继承于基类 object。
注意: 在C# 中一个类只能有一个基类,称为单继承。
● 虽然类只能直接继承一个基类, 但继承的层次没有限制。 也就说,作为基类的类可以派生自另外一个类, 而这个类又派生自另外一个类。 一直下去,直至最终到达object。
基类和派生类是相对的术语。 所有的类都是派生类,要么派生自object, 要么派生自其它的类。 所以, 通常当我们称一个类为派生类时, 我们的意思是它直接派生自某类而不是object。
注意: 编译器不允许派生类的可访问性高于基类, 也就说, 内部类可以继承于一个公共基类,但公共类不能继承于一个内部基类。
接口的定义
● 使用关键字 interface 来定义接口,语法为:
interface myInterface
{
//interface members
}
● 在默认情况下, 接口是内部的(internal)—— 即只有当前工程中的代码能够访问。 可以使用关键字 internal 写在 interface 的前面 来显式指定这一点。
● 还可以使用关键字public 指定接口是公共的—— 即可以在任何项目工程中的代码来访问。
● 不可以在接口中使用 abstract 和 sealed , 因为这两个关键字在接口中是没有意义的。
● 也可以使用 与类继承类似的方式来指定接口的继承。 主要区别是可以使用多个集接口:
interface IMyInterface : IMyBaseInterface, IMyBaseInterface2
{
// Interface members
}
接口不是类,所以没有继承object, 但是object 的成员 可以通过接口类型的变量来访问。 如上所述, 不能用实例化类的方式来实例化接口。
● 除了可以在类的后面指定基类外, 还可以在冒号的后面指定支持的接口。 如果指定了基类,它必须紧跟冒号之后, 之后才是接口。 如果未指定基类, 接口就紧跟在冒号的后面。 必须使用逗号来分隔基类名 ( 如果有基类的话 ) 和接口名。
class MyClass : MyBase,IMyBaseInterface, IMyBaseInterface2
{
// Class members
}
上述的类指定了一个基类,两个接口。 支持该接口的类必须实现所有接口的成员, 但如果不想使用给定的接口成员, 可以提供一种 “ 空” 的实现方式 (没有函数代码), 还可以把接口成员实现为抽象类中的抽象成员。
屏蔽基类成员
● 有时候我们要继承包含某个特殊方法的基类。 该方法虽然适合声明它的类, 但却不一定适合派生类。 在这种情况下, 我们希望在派生类中声明新成员屏蔽基类中的方法
下面看一个示例程序,仔细观察注释 和代码
namespace HelloWorld_Console
{
class SomeClass
{
// 注意: 基类中的成员 只有保护的和公有的才会被派生类隐藏, 私有的成员不会被隐藏。
public string Field1 = " 基类中的字段声明.";
public void Method1()
{
WriteLine($"基类中的Method1方法");
}
public static int Field2;
}
class OtherClass : SomeClass
{
new public static double Field2; //还可以屏蔽静态成员
// public int Field1 = 0;错误,虽然该字段的返回类型不同,名称跟基类中的相同,但是还是需要使用 new 前辍。
new public string Field1 = " 派生类中的字段声明.";
new public int Method1() // 虽然该成员函数的返回类型是int, 名称跟基类中的一样,但是还是需要前辍new
{// 通过在派生类中声明新得带有相同签名的成员函数,那么该派生类中的该函数会屏蔽掉基类中相同签名的成员函数。
/* 注意: 成员函数的签名由 名称和参数列表组成, 不包含返回类型。如果派生类中的 Method1 该函数添加一个
形参, 就不需要前辍new, 因为它们两个是不一样的函数签名 (C++ 中 与这里不一样) */
WriteLine($"派生类中的Method1方法");
return 0;
}
}
class Program
{
static void Main(string[] args)
{
OtherClass oc = new OtherClass();
oc.Method1();
ReadKey();
}
}
}
看一个基类引用的程序:
namespace HelloWorld_Console
{
class SomeClass
{
public string Field1 = " 基类中的字段声明.";
public void Method1()
{
WriteLine($"基类中的Method1方法");
}
public static int Field2;
}
class OtherClass : SomeClass
{
new public static double Field2; //还可以屏蔽静态成员
// public int Field1 = 0;错误,虽然该字段的返回类型不同,名称跟基类中的相同,但是还是需要使用 new 前辍。
new public string Field1 = " 派生类中的字段声明.";
new public int Method1() // 虽然该成员函数的返回类型是int, 名称跟基类中的一样,但是还是需要前辍new
{// 通过在派生类中声明新得带有相同签名的成员函数,那么该派生类中的该函数会屏蔽掉基类中相同签名的成员函数。
/* 注意: 成员函数的签名由 名称和参数列表组成, 不包含返回类型。如果派生类中的 Method1 该函数添加一个
形参, 就不需要前辍new, 因为它们两个是不一样的函数签名 */
WriteLine($"派生类中的Method1方法");
return 0;
}
}
class Program
{
static void Main(string[] args)
{
OtherClass oc = new OtherClass();
oc.Method1(); // 调用的是派生类中的该函数
SomeClass mySomeClass = (SomeClass)oc; //是一个基类引用派生类对象
mySomeClass.Method1(); // 这里调用的是基类中的该函数
ReadKey();
}
}
}
基类访问
如果在派生类中想访问被隐藏的基类中的继承成员, 可以在派生类中 显式用 base. 成员名 来访问基类中被隐藏的成员。
namespace HelloWorld_Console
{
class SomeClass
{
public string Field1 = " 基类中的字段声明.";
public void Method1()
{
WriteLine($"基类中的Method1方法");
}
}
class OtherClass : SomeClass
{
new public string Field1 = " 派生类中的字段声明.";
new public void Method1()
{
WriteLine(Field1);
WriteLine(base.Field1); //访问派生类中的字段
}
}
class Program
{
static void Main(string[] args)
{
OtherClass oc = new OtherClass();
oc.Method1();
SomeClass mySomeClass = (SomeClass)oc; //是一个基类引用派生类对象
mySomeClass.Method1(); // 这里调用的是基类中的该函数
ReadKey();
}
}
}
输出结果为:
派生类中的字段声明.
基类中的字段声明.
基类中的Method1方法