定义类
C#中类的定义
C#使用class关键字来定义类
class MyClass
{
//class members;
}
定义了一个类后就可以在项目中能访问到该类的位置实例化该类,默认情况下类的声明为内部的,即只有当前代码才能访问它,可使用internal关键字来显式指出这一点,但是没有必要
internal class MyClass
{
//class members.
}
此外还可以指定类是公共的,可被其他代码访问,为此要使用关键字public
public class MyClass
{
//class members.
}
除了这两个修饰符之外还有abstract和sealed修饰符
public abstract class MyClass //只能继承,不能实例化
public sealed class MyClass //不能继承
//这两个关键字是互斥的
还可以在类定义中指定继承
public class MyClass : MyBase
{
//Class members.
}
如果继承了一个抽象类,就必须实现所继承的所有抽象方法,一个类也只能有一个基类。
同时一个派生类的可访问性不能高于基类的可访问性,也就是说,内部类可以继承于一个公共基类,但公共基类不能继承于一个内部基类。下面代码是合法的
public class MyBase
{
//Class members.
}
internal class MyClass : MyBase
{
//Class members.
}
下面代码无法编译
internal class MyBase
{
//Class members.
}
public class MyClass : MyBase
{
//Class members.
}
支持某一接口的类必须实现所有的接口成员
类的实例可以调用一个继承自System.Object的方法ToString(),该方法会把对象的类名作为一个字符串返回,同时用该类名的名称空间来限定。
System.Object
所有类都继承于System.Object,所以这些类都可以访问该类中受保护的成员和公共成员,下面列出可供使用的成员有哪些。
方法 | 返回类型 | 虚拟 | 静态 | 说明 |
---|---|---|---|---|
Object() | N/A | 否 | 否 | 构造函数 |
~Object() | N/A | 否 | 否 | 析构函数 |
Equals(object) | bool | 是 | 否 | 把调用该方法的对象和另一个对象相比,如果他们相等就返回一个true,默认的实现代码会查看其对象参数是否应用了同一个对象,如果想以不同方式来比较对象,则可以重写该方法。 |
Equals(object,object) | bool | 否 | 是 | 比较传递给它的两个对象是否相等,比较时使用了上一条命令 |
ReferenceEquals(object,object) | bool | 否 | 是 | 比较传递给它的两个变量,看看他们是不是同一个实例的引用 |
ToString() | string | 是 | 否 | 返回一个对应于对象实例的字符串。默认情况下这是一个类类型的限定名称,但是可以重写它,给类类型提供合适的实现代码 |
MemberWiseClonoe() | object | 否 | 否 | 通过创建一个新对象的实例并复制成员,以复制该对象成员复制不会得到这些成员的新实例,新对象的任何引用类型的成员都将引用和源引用相同的对象,这个方法是受保护的所以只能在类或派生的类中使用。 |
GetType() | System.Type | 否 | 否 | 以System.Type的类型返回对象的类型 |
GetHashCode() | int | 是 | 否 | 在需要此参数的地方,用作对象的散列函数,它返回一个以压缩形式的标识对象状态的值。 |
构造函数和析构函数
在C#中定义类时常常不需要定义相关的构造函数和析构函数,因为在编写代码时,如果没有提供他们,编译器会自动提供他们。但如果有必要还是可以提供自己的构造函数和析构函数
class Myclass
{
public MyClass()
{
//Constructor code
{
}
接口和抽象类
相似之处:都包含可由派生类继承的成员,都不能直接实例化,但可以声明这些类型的变量,如果这样做,就可以使用多态性把继承这两种类型的对象指定给他们的变量,接着通过这些变量来使用这些类型的成员,但不能直接访问派生对象的其他成员。
区别之处:派生类只能继承自一个基类,即只能继承自一个抽象类(但可以用一个继承链包含多个抽象类)。相反类可以使用任意多个接口,但这不会产生太大的区别,两种情况取得的效果是相似的。
抽象类可以拥有抽象成员(没有代码体,且必须在派生类中实现,否则派生类本身必须也是抽象的)和非抽象成员(它们拥有代码体,也可以是虚拟的,这样就在派生类中重写)。接口成员是公共的(因为他们的目的是在外部使用),但抽象类的成员可以是私有的(只要他们不是抽象的),此外,接口不能包含字段、构造函数、析构函数、静态成员或常量。
抽象类主要用作对象系列的基类,这些对象共享某些主要特性,例如共同的目的和结构。接口则主要用于类,这些类存在根本性的区别,但仍可完成相同的任务。
结构类型
结构是值类型,而类是引用类型,具体区别可由以下代码展示
class MyClass
{
public int val;
}
struct myStruct
{
public int val;
}
class Problem
{
static void Main(String[] args)
{
MyClass objectA = new MyClass();
MyClass objectB = ObjectA;
objectA.val = 10;
objectB.val = 20;
myStruct structA = new myStruct();
myStruct structB = structA;
structA.val = 30;
structB.val = 40;
WriteLine($"objectA.val = {objectA.val}");
WriteLine($"objectB.val = {objectB.val}");
WriteLine($"structA.val = {structA.val}");
WriteLine($"structB.val = {structB.val}");
ReadKey();
}
}
运行程序代码可得
objectA.val = 20
objecrB.val = 20
structA.val = 30
structB.val = 40
类是引用对象,objectA和objectB两个变量就都包含同一个对象的指针。
浅度和深度复制
简单按照成员复制对象可以通过System.Object的MemberwiseClone()方法来完成,这是一个受保护的方法,很容易在对象上定义一个调用该方法的公共方法。这个方法提供的功能叫做浅度复制,因为它并没有考虑到引用类型成员。因此,新对象中的引用成员就会指向源对象中相同成员引用的对象。这在许多情况下并不理想,如果要创建成员的新实例(复制值,而不复制引用),此时需要使用深度复制。
可以实现一个ICloneable接口,以标准方式进行深度复制。如果使用这个方法,就必须实现它包含的Clone()方法。