C#代码示例_定义类

时间:2023-03-09 09:54:57
C#代码示例_定义类

默认情况下,类声明为内部的,即只有当前项目中的代码才能访问它。可以使用internal访问修饰符关键字显示指定。

除了两个访问修饰符关键字(public, internal)外,还可以指定类是抽象的(不能实例化,只能继承,可以有抽象成员)或密封的(sealed,不能继承)。为此,可以使用两个互斥的关键字abstract或sealed。

编译器不允许派生类的可访问性高于基类。

不能再接口中使用关键字abstract和sealed,因为这两个修饰符在接口定义中是没有意义的(它们不包含实现代码,所以不能直接实例化,且必须是可以继承的)。

在.NET中使用的析构函数(由System.Object类提供)叫做Finalize(),但这不是我们用于声明析构函数的名称。使用下面的代码,而不是重写Finalize():

class MyClass

{

~MyClass()

{

// Destructor body.

}

}

类的析构函数由带有~前缀的类名(与构造函数相同)来声明。

无论在派生类上使用什么构造函数(默认的构造函数或非默认的构造函数),除非明确指定,否则就使用基类的默认构造函数。

base关键字指定.NET实例化过程使用基类中有指定参数的构造函数。

除了base关键字外,这里还可以将另一个关键字this用作构造函数初始化器。这个关键字指定在调用指定的构造函数前,.NET实例化过程对当前类使用非默认的构造函数。例如:

public class MyDerivedClass : MyBaseClass

{

public MyDerivedClass() : this(5,6)

{

}

...

public MyDerivedClass (int i, int j) : base (i)

{

}

}

这段代码将执行下述序列:

执行System.Object.Object()构造函数。

执行MyBaseClass.MyBaseClass(int i)构造函数。

执行MyDerivedClass.MyDerivedClass(int i, int j)构造函数。

执行MyDerivedClass.MyDerivedClass()构造函数。

包含关系:一个类包含另一个类。这类似于继承关系,但包含类可以控制对被包含类的成员的访问,甚至在使用被包含类的成员前进行其他处理。

集合关系:一个类用作另一个类的多个实例的容器。这类似于对象数组,但集合有其他功能,包括索引、排序和重新设置大小等。

值类型和引用类型的一个主要区别是:值类型总是包含一个值,而引用类型可以是null,表示它们不包含值。但是,可以使用可空类型(这时泛型的一种形式)创建一个值类型,使值类型在这个方面的行为方式类似于引用类型(即可以为null)。

只有string和object简单类型是引用类型,但数组也是隐式的引用类型。

如果一个项目什么都不包含,只包含类(以及其他相关的类型定义,但没有入口点),该项目就称为类库。

类库项目编译为.dll程序集,在其他项目中添加对类库项目的引用,就可以访问它的内容,这将扩展对象提供的封装性。

应用程序使用外部库中定义的类时,可以把该应用程序称为库的客户应用程序。使用所定义的类的代码一般简称为客户代码。

接口和抽象类

相同点:都包含可以由派生类继承的成员。都不能直接实例化,但可以声明这些类型的变量。

不同点:派生类只能继承一个基类,即只能直接继承一个抽象类。而类可以使用任意多个接口。抽象类可以拥有抽象成员(没有代码体,且必须在派生类中实现,否则派生类本身必须也是抽象的)和非抽象成员(它们拥有代码体,也可以是虚拟的,这样就可以在派生类中重写)。另一方面,接口成员必须都在使用接口的类上实现——它们没有代码体。另外,按照定义,接口成员是公共的(因为它们倾向于在外部使用),但抽象类的成员可以是私有的(只要它们不是抽象的)、受保护的、内部的或受保护的内部成员(其中受保护的内部成员只能在应用程序的代码或派生类中访问)。此外,接口不能包含字段、构造函数、析构函数、静态成员或常量。

抽象类主要用作对象系列的基类,共享某些主要特性,例如,共同的目的和结构。接口则主要用于类,这些类在基础水平上有所不同,但仍可以完成某些相同的任务。

浅度和深度复制

浅度复制(shallow copy),没有考虑引用类型成员。因此,新对象中的引用成员就会指向与源对象中相同成员的对象。可以通过派生于System.Object的MemberwiseClone()方法来完成。

深度复制(deep copy),创建成员的新实例(复制值,而不复制引用)。可以实现ICloneable接口,实现其中包含的Clone()方法。这个方法返回一个类型为System.Object的值。

成员的访问级别:

  • public——成员可以由任何代码访问。
  • private——成员只能由类中的代码访问(如果没有使用任何关键字,就默认使用这个关键字)。
  • internal——成员只能由定义它的程序集(项目)内部的代码访问。
  • protected——成员只能由类或派生类中的代码访问。

后两个关键字可以合并使用,所以也有protected internal成员。它们只能由项目(更确切地讲,是程序集)中派生类的代码来访问。

定义字段

字段也可以使用关键字readonly,表示这个字段只能在执行构造函数的过程中赋值,或由初始化赋值语句赋值。例如:

class MyClass

{

Public readonly int MyInt=17;

}

定义方法

可以在方法定义中使用下述关键字:

  • virtual——方法可以重写。
  • abstract——方法必须在非抽象的派生类中重写(只用于抽象类中)。
  • override——方法重写了一个基类方法(如果方法被重写,就必须使用该关键字)。
  • extern——方法定义在其他地方。
     public class MyBaseClass
{
public virtual void DoSomething()
{
// Base implementation.
}
} public class MyDerivedClass : MyBaseClass
{
public override sealed void DoSomething()
{
// Derived class implementation, override base implementation.
}
} }

如果使用了override, 也可以使用sealed指定在派生类中不能对这个方法作进一步的修改,即这个方法不能由派生类重写。

定义属性

属性拥有两个类似于函数的块,一个块用于获取属性的值,另一个块用于设置属性的值。这两个块也称为访问器,分别用get和set关键字来定义,可以用于控制对属性的访问级别。例如:

private int myInt;

public int MyIntProp

{

get

{

return myInt;

}

protected set

{

myInt=value;

}

}

属性可以使用virtual、override和abstract关键字,就像方法一样,但这几个关键字不能用于字段。访问其可以有自己的可访问性。

访问器可以使用的访问修饰符取决于属性的可访问性,访问器的可访问性不能高于它所属的属性。也就是说,私有属性对它的访问器不能包含任何可访问修饰符,而公共属性可以对其访问器使用所有的可访问修饰符。

“重构”表示使用工具修改代码,而不是手工修改。

如:为字段创建属性:右击该字段,选择Refactor|Encapsulate Field。

自动属性

public int MyIntProp { get; set; }

自动属性的唯一限制是它们必须包含get和set存取器,无法使用这种方式定义只读或只写属性。

隐藏基类方法

当从基类继承一个(非抽象的)成员时,也就继承了其实现代码。如果继承的成员是虚拟的,就可以用override关键字重写这段实现代码。无论继承的成员是否为虚拟,都可以隐藏这些实现代码。

使用new关键字可以显示隐藏基类方法。

 namespace test
{
public class MyBaseClass
{
public virtual void DoSomething()
{
Console.WriteLine("Base imp");
}
} public class MyDerivedClass : MyBaseClass
{
new public void DoSomething()
{
Console.WriteLine("derived imp");
}
} class Program
{
static void Main(string[] args)
{
MyDerivedClass myObj = new MyDerivedClass();
MyBaseClass myBaseObj;
myBaseObj = myObj;
myBaseObj.DoSomething(); Console.ReadLine(); }
}
}

其结果如下: Base imp
基类方法不必是虚拟的,但结果是一样的。

调用重写或隐藏的基类方法

使用base关键字,表示包含在派生类中的基类的实现代码(载控制构造函数时,其用法是类似的)。

因为base使用的是对象实例,所以在静态成员中使用它会产生错误。

还可以使用this关键字。与base一样,this也可以用在类成员的内部,且该关键字也引用对象实例。只是this引用的是当前的对象实例(即不能在静态成员中使用this关键字)。

This关键字最常用的功能是把当前对象实例的引用传递给一个方法,如下例所示:

public void doSomething()

{

MyTargetClass myObj=new MyTargetClass();

myObj.DoSomethingWith(this);

}

其中,被实例化的MyTargetClass实例有一个DoSomethingWith()方法,该方法带一个参数,其类型与包含上述方法的类兼容。这个参数类型可以是类的类型、由这个类继承的类类型,或者由这个类或System.Object实现的一个接口。

接口成员的定义与类成员的定义相似,但有几个重要的区别:

  • 不允许使用访问修饰符(public, private, protected 或 internal),所有的接口成员都是公共的。
  • 接口成员不能包含代码体。
  • 接口不能定义字段成员。
  • 接口成员不能用关键字static, virtual, abstract 或 sealed来定义。
  • 类型定义成员是禁止的。

可以关键字new来隐藏继承了基接口的成员。例如:

interface IMyBaseInterface

{

void DoSomething();

}

interface IMyDerivedInterface : IMyBaseInterface

{

new void DoSomething();

}

在接口中定义的属性可以定义访问块get和set中的哪一个能用于该属性(或将它们同时用于该属性)。

接口可以定义为类的成员(但不能定义为其他接口的成员,因为接口不能包含类型定义)。

可以使用关键字virtual或abstract来实现接口成员。

可以使用关键字virtual或abstract来实现接口成员,但不能使用static或const。还可以在基类上实现接口成员,例如:

  public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
} public class MyBaseClass
{
public void DoSomething()
{
}
} public class MyDerivedClass : MyBaseClass, IMyInterface
{
public void DoSomethingElse()
{
}
}

继承一个实现给定接口的基类,就意为着派生类隐式地支持这个接口。

显示实现接口成员

如果显示实现接口成员,该成员就只能通过接口来访问,不能通过类来访问。隐式成员可以通过类和接口来访问。

     public interface IMyInterface
{
void DoSomething();
void DoSomethingElse();
} public class MyClass : IMyInterface
{
void IMyInterface.DoSomething()
{
}
public void DoSomeThingElse()
{
}
}

其中DoSomething()是显示实现的,而DoSomethingElse()是隐式实现的。

用非公共的可访问性添加属性存储器

如果实现带属性的接口,就必须实现匹配get/set存储器。这并不是绝对正确——如果在定义属性的接口中只包含set块,就可给类中的属性添加get块,反之亦然。但是,只有所添加的存储器的可访问修饰符比接口中定义的存储器的可访问修饰符更严格时,才能这么做。因为按照定义,接口定义的存储器是公共的,也就是说,只能添加非公共的存储器。

部分类定义(partial class definition)

把类得定义放在多个文件中。为此,只需在每个包含部分类定义的文件中对类使用partial关键字既可。例如:

public partial class MyClass

{

......

}

部分类对Windows应用程序隐藏与窗体布局相关的代码有很大的作用。在Form1类中,Windows窗体的代码存储在Form1.cs和Form1.Designer.cs中。

应用于部分类的接口也会应用于整个类。

部分类定义可以在一个部分类定义文件或者多个部分类定义文件中包含基类。但如果基类在多个定义文件中指定,它就必须是同一个基类,因为在C#中,类只能继承一个基类。

部分方法定义

部分类也可以定义部分方法。部分方法在部分类中定义,但没有方法体,在另一个部分类中包含实现代码。在这两个部分类中,都要使用partial关键字。例如:

public partial class MyClass

{

partial void MyPartialMethod();

}

public partial class MyClass

{

partial void MyPartialMethod()

{

// Method implementation
    }
}

部分方法也可以是静态的,但它们总是私有的,且不能有返回值。他们使用的任何参数都不能是out参数,但可以是ref参数。部分方法也不能使用virtual、abstract、override、sealed和extern修饰符。

实际上,部分方法在编译代码时非常重要,其用法倒并不重要。编译代码时,如果代码包含一个没有实现代码的部分方法,编译器会完全删除该方法,还会删除对该方法的所有调用。执行代码时,不会检查实现代码,因为没有检查方法的调用。这会略微提高性能。

指定项目为解决方案的启动项目

在Solution Explorer窗口中右击该项目名,选择Set as StartUp Project菜单项。

相关文章