[CSharp]4 封装

new关键字

使用new关键字来分配对象

为什么要用new来分配对象呢?在我们通过new关键字把引用赋给对象之后,这个应用才会指向内存中的有效类示例。对象必须使用new关键字来分配到内存,如果我们不用new关键字,并且在之后的代码语句中尝试使用类变量的话,就会收到来自编译器的错误。

构造函数

构造函数永远不会有返回值,即使是void,并且它的名字总是和需要构造的类的名字相同。构造函数可以有多个,让它们彼此不同的是构造函数参数的个数和类型。当我们定义了具有相同名字但参数数量和类型不同的方法时,就是重载方法。

this关键字

this关键字可以用来解决当传入参数名字和类型数据字段的名字相同时产生的作用域歧义。如果没有歧义的话,其实也不需要在类访问它自己的数据或成员时使用this关键字。this关键字的另一个用法是使用一项名为构造函数链的技术来设计类。而一个更简洁的方案是让一个接受最多参数个数的构造函数来做“主构造函数”,并且实现必须的验证逻辑。

代码如下:

class Motorcycle

{

public int driveIntensity;

public string driveName;

public Motorcycle(){ }

public Motorcycle(int intensity):this(intensity,""){ }

public Motorcycle(string name):this(0,name){ }

public Motorcycle(int intensity,string name)

{

if(intensity>10)

{   intensity=10;   }

driveIntensity=intensity;

driveName=name;

}

.......

}

构造函数的逻辑流程如下:

  1. 通过调用只有单个int的构造函数来创建对象。
  2. 构造函数将提供的数据转发给主构造函数,并且提供调用者没有提供的其他初始参数。
  3. 主构造函数把传入的数据赋值给对象的字段数据。
  4. 控制返回到最初调用的构造函数,并且执行所有剩余的代码语句。

可选参数

使用可选参数的单个构造函数示例:

public Motorcycle(int intensity=0,string name=" ") { }

static关键字

通过使用static关键字来定义许多静态成员,如果这样的话,这些成员就只能直接从类级别而不是对象引用调用。比如,System.Console,我们没有从对象级别调用WriteLine()方法。简而言之,静态方法被(类设计者)认为是非常普遍的项,并且不需要在调用成员时创建类型的实例。虽然任何类都可以定义静态方法,但他们通常出现在“工具类”中。根据定义,工具类是不维护任何对象级别的状态且并非由new关键字创建的类。因此,工具类会以类级别(即静态)成员公开所有功能。static关键字可用于:类的数据,类的方法,类的属性,构造函数,整个类定义。

谨记:静态数据字段是由所有对象共享的。

如果同种类的所有对象都经常使用某个值,也非常适合使用静态数据。静态构造函数是特殊的构造函数,并且非常适用于初始化在编译时未知的静态数据的值(例如,我们需要从外部文件读取或者生成随机数等)。

  1. 一个类只可以定义一个静态构造函数。也就是说静态构造函数不能被重载。
  2. 静态构造函数不允许访问修饰符并且不能接受任何参数。
  3. 无论创建了多少类型的对象,静态构造函数也只执行一次。
  4. 运行库创建类实例或调用者首次访问静态成员之前,运行库会调用静态构造函数。
  5. 静态构造函数的执行先于任何实例级别的构造函数。

如果一个类被定义为静态的,就不能使用new关键字来创建,并且只能包含static关键字标记的成员或字段。(只包含静态功能的类或结构通常称为工具类)

C#访问修饰符
public 类型或者类型成员 公共的项没有限制。公共成员可从对象以及任何派生类访问。公共类型可以从其他外部程序集进行访问。
private 类型成员或者嵌套类型 私有项只能由定义它们的类(或结构)进行访问
protected 类型成员或者嵌套类型 受保护项可以由定义它们的类及其任意子类使用,但外部类无法通过C#的点操作符访问
internal 类型或者类型成员 内部项只能在当前程序集访问。因此,如果我们在.NET类库中定义一组内部类型的话,其它程序集就不能使用它们       
protected internal 类型成员或者嵌套类型 如果在一个项上组合protected和Internal关键字,项在定义它们的程序集、类以及派生类中可用

C#的封装服务

表示对象状态的成员都不应该标记为公共的。

如果在类中定义class Pay{... private int currPay; ...}在类外方法中Pay pay=new Pay(); pay.currPay=10;则会导致编译器错误。如果希望外部世界和私有字段进行交互,传统的做法是定义访问方法(即get方法)和修改方法(即set方法)。

class Pay

{

...

private int currPay;

public int GetPay()

{

return currPay;

}

public int SetPay(string currpay)

{

currPay=currpay;

}

...

}

但是.NET语言还是提倡使用属性来强制数据封装状态数据。首先,理解属性总是映射到“实际的”访问方法和修改方法。因此,类的设计者还是可以在值赋值之前执行任何必要的内部逻辑(比如,对值进行大写转换,过滤值中的不合法字符,检查数字值的边界等)。比如:

class Employee

{

private string empName;

public string Name

{

get { return empName;  }

set

{

if(value.Length>15)

{   Console.WriteLine("Error! Name must be less than 16 characters!");   }

else

{   empName=value;   }

}

}

注意,属性通过返回值指定了它所封装的数据类型。而且属性在定义时没有使用括号(甚至空括号)。在属性的set作用域中,我们使用了value标记,它用来表示调用者设置属性时传入的值。该标记不是真正的C#关键字,而是上下文关键字。

自动属性

在创建属性封装数据的过程中,大多数C#属性都在set作用域中包含业务逻辑,但有时只需要简单的获取和设置值,而不需要实现任何逻辑。这意味着你能够以如下所示的代码结尾:

public string musicName

{

get { return musicName; }

set { musicName=value; }

}

而使用自动属性语法可以快速创建该属性。例如:public string musicName { get; set; }

与传统的C#属性不同的是,不允许构建只读或只写的自动属性,必须同时支持读写功能。

先创建如下class:

class Car

{  public string PetName { get;  set; }

public int Speed { get; set; }

public string Color { get; set; }

public void DisplayStats()

{

Console.WriteLine("Car Name:{0}",PetName);

Console.WriteLine("Speed:{0}",Speed);

Console.WriteLine("Color:{0}",Color);

}

}

class Garage

{   public int NumberOfCars { get; set; }

public Car MyAuto { get; set; }

}

由于C#会为字段数据提供默认值,你能知道NumberOfCars的值(自动设置为0),但当你直接调用MyAuto时,将会在运行时得到“空引用异常”。这是因为没有为后台使用的Car成员变量设置新的对象。由于私有字段的返回字段是在编译时创建的,所以你不能使用C#的字段初始化语法用new关键字直接分配引用类型。这项工作必须在类构造函数内部执行,以确保对象以安全的方式诞生。例如:

class Garage

{  public int NumberOfCars { get; set; }

public Car MyAuto { get; set; }

public Garage()

{

MyAuto=new Car();

NumberOfCars=1;

}

public Garage(Car car,int number)

{

MyAuto=car;

NumberOfCars=number;

}

}

个更新之后就可以像下面这样将Car对象传入Garage对象:

Car c=new Car();

c.PetName="BMW";

c.Speed=55;

c.Color="Red";

c.DisplayStats();

Garage g=new Garage();

g.MyAuto=c;

Console.WriteLine("Number of Cars in garage:{0}",g.NumberOfCars);

Console.WriteLine("Your car is named:{0}",g.MyAuto.PetName);

}

对象初始化

Point point =new Point { X=30,Y=30};

常量数据

public const double PI=3.14;

常量字段是隐式静态的。定义常量时必须为常量指定初始值。

class MyMathClass

{   public const double PI;

public MyMathClass()

{   PI=3.14;  }

}

我们将收到编译时错误,有这种限制是因为在编译时必须知道常量的值,构造函数是在运行时调用的。

只读字段

和静态字段不同,只读字段不是隐式静态的。因此如果要从类级别公开PI,就必须显示使用static关键字。

分布类型

由于字段数据、类型属性和构造函数在生成过程中很少变动,而方法却需要经常修改。

例如:

Employee.cs文件中:

partial class Employee

{

//  方法

//  属性

}

Employee.Internal.cs文件:

partial class Employee

{

//  字段数据

//   构造函数

}

小结

本章的宗旨是介绍C#类类型的作用。我们看到,类可以接受任意构造函数来允许对象用户在创建对象时创建状态。本章还演示了几个类设计技术(以及相关关键字)。回忆一下,this关键字可以用于访问当前对象,static关键字可以用于在类(不是对象)级别定义字段和成员,而const关键字(以及readonly修饰符)可以用来定义在赋值初始值之后永远不能改变的数据点。

时间: 2024-10-13 16:54:50

[CSharp]4 封装的相关文章

CSharp任何可比较的数据类型(大小比较泛型实现方法)封装

1 /// <summary> 2 /// 判定A大于B(A.CompareTo(B)<0) 3 /// </summary> 4 /// <typeparam name="T">实现了 IComparable 接口的任意对象 </typeparam> 5 /// <param name="a">比较对象A</param> 6 /// <param name="b"

C# Socket流数据大小端读写封装

网络数据是大端模式,而c#中的数据小端结构,那么在读写网络数据的时候需要进行转换.c#类库IPAddress已经封装了大小端的转换. 封装代码如下: [csharp] view plain copy using System.IO; using System.Net; using System; namespace Framework { public class NetStream { private MemoryStream stream; private BinaryReader read

微信公众号开发系列-Http请求封装基类

HttpHelper请求封装基类,支持get请求和POS请求,方便微信开发接口交互,为后面接口交互做准备. 1.HttpHelper帮助基类 [csharp] view plaincopy using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Net; using System.Net.Security; namespa

【转】C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装

http://blog.csdn.net/sqldebug_fan/article/details/17557341 1.SocketAsyncEventArgs介绍 SocketAsyncEventArgs是微软提供的高性能异步Socket实现类,主要为高性能网络服务器应用程序而设计,主要是为了避免在在异步套接字 I/O 量非常大时发生重复的对象分配和同步.使用此类执行异步套接字操作的模式包含以下步骤:1.分配一个新的 SocketAsyncEventArgs 上下文对象,或者从应用程序池中获

在DLL中封装的VCL窗体Tab键响应的问题

在DLL中的子窗体不会响应Tab按键的,这个时候就需要手动去指定Tab键的操作,但是前提是主窗体要向这个窗体发送一个消息,一个Tab键按下的消息.基本顺序是这样的: 1. 主窗体用Hook技术捕获Tab按键并向活动子窗体发送一个Tab键按下的消息 2. 子窗体手动定义一个Tab键被按下的处理(需要用到FormKeyDown事件). 3. 移动到下一个焦点的WinAPI函数:Perform(WM_NEXTDLGCTL,0,0); 4.移动到上一个焦点的函数:Perform(WM_NEXTDLGCT

C#高性能大容量SOCKET并发(二):SocketAsyncEventArgs封装

1.SocketAsyncEventArgs介绍 SocketAsyncEventArgs是微软提供的高性能异步Socket实现类,主要为高性能网络服务器应用程序而设计,主要是为了避免在在异步套接字 I/O 量非常大时发生重复的对象分配和同步.使用此类执行异步套接字操作的模式包含以下步骤:1.分配一个新的 SocketAsyncEventArgs 上下文对象,或者从应用程序池中获取一个空闲的此类对象.2.将该上下文对象的属性设置为要执行的操作(例如,完成回调方法.数据缓冲区.缓冲区偏移量以及要传

asp.net mvc 自定义pager封装与优化

asp.net mvc 自定义pager封装与优化 Intro 之前做了一个通用的分页组件,但是有些不足,从翻页事件和分页样式都融合在后台代码中,到翻页事件可以自定义,再到翻页和样式都和代码分离, 自定义分页 pager 越来越容易扩展了. HtmlHelper Pager扩展 Pager V1.0 : 1 /// <summary> 2 /// Pager V1.0 3 /// </summary> 4 /// <param name="helper"&

csharp编写界面,opencv编写类库,解决 Pinvoke过程中的参数传递和平台调用问题

使用csharp 编写winform程序,不仅速度快,而且容易界面美化并找到其他类库的支持:而使用 opencv编写图形图像处理程序,是目前比较流行,而且高效的一种方法.如果需要将两者结合,需要解决的问题就是使用 csharp调用vc 下编写的库文件.两个难点,一个是平台调用的内存控制问题,一个是参数传递问题.关注点在解决实际问题 在现实中,我发现问题比较大的是两点,一点是内存控制问题,一个是平台调用问题. 一.内存控制:(1-6种方法是我验证后失败的方法,关注问题解决者可直接看第7点) 1.验

C#封装、多态、抽象、接口、匿名方法等学习

1:封装 将对象进行封装,并不等于将整个对象完全包裹起来,而是根据实际需要,设置一定的访问权限,用户根据不同的权限调用对象提供的功能,在C#语言中,可以使用修饰符public.internal.protected.private分别修饰类的字段.属性和方法. 2:继承,主要是注意继承的格式 [csharp] view plaincopy public class ParentClass  //父类 { public ParentClass(); } public class ChildClass