目录结构:

contents structure [+]

接下来笔者将会对C#中的方法做详细阐述。

1.构造函数

我们首先来讲解一下C#中的构造函数,C#中的数据类型大致分为两类,分别是引用数据类型和值类型。构造函数又分为实例构造函数,和静态构造函数。

1.1 引用类型的构造函数

这种构造函数是最常见的,看下面的栗子:

    class Program
{
//无参构造器
public Program() {
}
//带参数数组的构造器
public Program(Object[] para) {
}
//静态构造器
static Program() {
}
}

如果一个类中无构造器,那么会默认生成一个无参的构造器。

1.2 值类型的构造函数

值类型(Struct)构造器与引用类型构造器的工作方式完全不同,值类型总是运行创建值类型的构造器,并且允许值类型的实例化。
在值类型中使用构造器有如下几点需要注意:
1.值类型中不能显示定义无参构造器
2.C#中定义的构造器,必需显示调用才会执行。(C#的值类型默认不会调用值类型的无参构造器)
3.类型构造器中的字段只能访问静态字段

我们来看如下这个栗子:

    struct Point {
public Int32 m_x, m_y; public Point() {
m_x = m_y = ;
}
}
class Rectangle {
public Point m_topLeft, m_bottomRight; public Rectangle() { }
}

上面程序中m_x,m_y的值既不是0,也不是5。编译器会直接报错,C#中是不允许在值类型中定义无参构造器的。

2. 析构函数

2.1 析构函数的使用

析构函数和构造函数的功能恰好相反,构造函数的作用是创建对象所需要的资源,而析构函数的作用是在对象销毁时回收资源。
注意:
1.不能在结构中定义析构函数,只能在类中使用析构函数。
2.一个类只能有一个析构函数。
3.无法继承或重载析构函数。
4.析构函数既没有修饰符,也没有参数。
5.不应使用空析构函数。如果类包含析构函数,Finalize 队列中则会创建一个项。调用析构函数时,将调用垃圾回收器来处理该队列。如果析构函数为空,只会导致不必要的性能损失。
6.程序员无法控制何时调用析构函数,因为这是由垃圾回收器决定的。垃圾回收器检查是否存在应用程序不再使用的对象。如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有)并回收用来存储此对象的内存。程序退出时也会调用析构函数。
7.可以通过调用 Collect 强制进行垃圾回收,但大多数情况下应避免这样做,因为这样会导致性能问题。

例如:
例如,下面是类 Car 的析构函数的声明:

class Car
{
~Car() // destructor
{
// cleanup statements...
}
}

该析构函数隐式地对对象的基类调用 Finalize。这样,前面的析构函数代码被隐式地转换为以下代码:

protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}

这意味着对继承链中的所有实例递归地调用 Finalize 方法(从派生程度最大的到派生程度最小的)。

如果您的应用程序在使用昂贵的外部资源,我们还建议您提供一种在垃圾回收器释放对象前显式地释放资源的方式。可通过实现来自 IDisposable 接口的 Dispose 方法来完成这一点,该方法为对象执行必要的清理。这样可大大提高应用程序的性能。即使有这种对资源的显式控制,析构函数也是一种保护措施,可用来在对 Dispose 方法的调用失败时清理资源。

    class First
{
~First()
{
Console.WriteLine("First's destructor is called.");
}
} class Second : First
{
~Second()
{
Console.WriteLine("Second's destructor is called.");
}
} class Third : Second
{
~Third()
{
Console.WriteLine("Third's destructor is called.");
}
} class TestDestructors
{
static void Main()
{
Third t = new Third();
t = null;
GC.Collect();//强制回收
Console.ReadLine();
}
}

输出为:

Third's destructor is called.
Second's destructor is called.
First's destructor is called.

2.2 析构函数和Dispose()方法的区别

1. Dispose需要实现IDisposable接口。
2. Dispose由开发人员代码调用,而析构函数由GC自动调用。
3. Dispose方法应释放所有托管和非托管资源。而析构函数只应释放非托管资源。因为析构函数由GC来判断调用,当GC判断某个对象不再需要的时候,则调用其析构方法,这时候该对象中可能还包含有其他有用的托管资源。
4. 通过系统GC频繁的调用析构方法来释放资源会降低系统性能,所以推荐显示调用Dispose方法。
5. Dispose方法结尾处加上代码“GC.SuppressFinalize(this);”,即告诉GC不需要再调用该对象的析构方法,否则,GC仍会在判断该对象不再有用后调用其析构方法,虽然程序不会出错,但影响系统性能。
6、析构函数 和 Dispose 释放的资源应该相同,这样即使类使用者在没有调用 Dispose 的情况下,资源也会在 Finalize 中得到释放。
7、Finalize 不应为 public。
8、有 Dispose 方法存在时,应该调用它,因为 Finalize 释放资源通常是很慢的。
9、使用using可以自动调用对象的Dispose方法。

3.操作符重载

许多编程语言(比如C#)允许定义操作符应该如何操作类型的实例。例如,许多类型都重载了相等(==)和不等(!=)操作符。CLR对操作符重载一无所知,他甚至不知道什么是操作符。是编程语言定义了每个操作符的含义,已经当这些特殊符号出现时,应该生成什么样的代码。

CLR要求定义操作符的方法必须声明为public和static,例如:

    class Complex {
public static Complex operator+(Complex c1,Complex c2) {
...
}
}

用ildasm工具打开上面编译好的类,可以看到如下图所示:

从这里我们知道,操作符+ 会转化为 op_addition 方法。
下面列出C#中的一元和二元操作符,及其对应的特殊名:
C#的一元操作符:

C#操作符 特殊方法名
+ op_UnaryPlus
- op_UnaryNegation
! op_LogicalNot
~ op_OnesComplement
++ op_Increment
-- op_Decrement
  op_True
  op_False

C#的二元操作符:

C#操作符 特殊方法名
+ op_Addition
- op_Subtraction
* op_Multiply
/ op_Division
% op_Modulus
& op_BitwiseAnd
| op_BitwiseOr
^ op_ExclusiveOr
<< op_LeftShift
>> op_RightShift
== op_Equality
!= op_Inequality
< op_LessThan
> op_GreaterThan
<= op_LessThanOrEqual
>= op_GreaterThanOrEqual

4.转化操作符方法

有时需要将对象从一种类型转化为另一种类型,当源类型和目标类型都是编译器识别的基元类型时,编译器自己就知道如何生成转化对象所需要的代码。

CLR要求转化操作符必须定义为public和static。在C#中,implicit关键字告诉编译器为了生成代码来调用方法,不需要在源代码中进行显示转化(隐式转化);explicit关键字告诉编译器需要在源代码中进行强制转化(显示转化)。
在implicit和explicit关键字之后,要指定operator关键字告诉编译器该方法是一个转化操作符。在operator指定要转化成什么类型,在圆括号内,则指定从什么类型转化。
栗子:

    class Program
{
static void Main()
{
Rational r1 = ;//隐式转化
Int32 i = (Int32)r1;//显示转化
Console.WriteLine(i.ToString());
Console.ReadLine();
}
}
public sealed class Rational {
private Int32 num;
//由一个Int32构造一个Rational
public Rational(Int32 num) {
this.num = num;
}
//指定从Int32到Radional为隐式转化
public static implicit operator Rational(Int32 i) {
return new Rational(i);
}
//指定从Radional到Int32为显示转化
public static explicit operator Int32(Rational r) {
return r.num;
}
}

使用ildasm工具打开上面的程序,会发现Ration显示转化方法的元数据为:

我们可以看出IL代码为:

public static Rational op_Implicit(Int32 num)
public static Int32 op_explicit(Rational)

5.扩展方法

C#中允许扩展方法的使用,看如下栗子:

    class Program
{
static void Main()
{
Program p = new Program();
p.Print();
Console.ReadLine();
}
}
static class PrintClass {
//定义一个扩展方法
public static void Print(this Program program, Int32 count)
{
for (Int32 i = ; i < count; i++)
{
Console.WriteLine(i);
}
}
}

使用C#的时候需要注意:
1.C#只支持扩展方法,不支持扩展属性、扩展事件、扩展操作符
2.扩展方法(第一个参数前面有this关键字)必需在非泛型的静态类中声明。
3.C#编译器在静态类中查找扩展方法时,要求静态类本身具有文件作用域。如果静态类嵌套在另一个类中,那么C#编译器就显示错误消息:“扩展方法必须在顶级静态类中定义,....”
4.多个静态类可以定义相同的扩展方法,如果编译器检测到存在两个或多个相同的扩展方法,那么就会报错。
5.扩展方法可能存在版本控制的问题。例如:在未来的版本中,为上面案例中Program类定义了Print实例方法,那么再重新编译我的代码,Print方法就会被绑定到Program的Print实例方法,这而不是静态的Print方法,显然这不是我期望的,程序可能会出现不可预期的行为。

6.分部方法(partial)

C#中提供了分布方法的功能,它允许方法的定义和方法的使用在不同的源文件中,
栗子:

    //工具生成的代码,存储在某个源代码文件中
internal sealed partial class Base {
private String m_name;
//声明分部方法
partial void OnNameChanging(String value); public String Name {
get { return m_name; }
set {
OnNameChanging(value);//通知类要进行更改了
m_name = value;//更改字段
}
}
}
    //开发人员生成的代码,存在另一个源代码文件中
internal sealed partial class Base
{
//这是分部方法的实现,会在m_name更改前调用
partial void OnNameChanging(string value)
{
if (String.IsNullOrEmpty(value)) {
throw new ArgumentException("value");
}
}
}

分部方法实现了在尽量少地破坏原来的代码的情况下,增加新的功能。
使用分部方法需要注意以下几点:
1.在使用分部方法的时,要进行方法的声明,要用partial关键字标记。
2.实现分部方法的声明时,要用partial关键字标记。
3.分部方法的返回类型始终是void,任何参数都不能用out来标记。
4.分部方法的声明和实现必需有完全一致的声明。
5.分部方法总是被视为private,但是C#编译器禁止在分部方法前添加private关键字。

7.外部方法(extern)

修饰符用于声明在外部实现的方法。extern 修饰符的常见用法是在使用 Interop 服务调入非托管代码时与 DllImport 属性一起使用;在这种情况下,该方法还必须声明为 static,
如下面的示例所示:

[DllImport("avifil32.dll")]
private static extern void AVIFileInit();

栗子:

        [DllImport("User32.dll",EntryPoint="MessageBox",CharSet=CharSet.Unicode)]
public static extern int MyMessageBox(int h, string m, string c, int type);
//hovertree.com
static int Main()
{
string myString;
Console.Write("Enter your message: ");
myString = Console.ReadLine();
return MyMessageBox(, myString, "My Message Box", );
}

在上面的栗子中,DllImport引用的文件是User32.dll,这里还可以简化掉后缀,直接写为"User32"。

【C#】C#中方法(函数)的类型有哪些的更多相关文章

  1. C语言中函数返回字符串的4中方法

    C语言中函数返回字符串的4中方法 函数的构成部分:返回类型.函数名称.参数.函数主体 参数:函数调用时传入的参数称为实参,函数定义时出现的参数为形参 形参的作用在于接收实参传入的值,形参和函数内部的其 ...

  2. C#--方法的参数类型

    在C#中,方法的参数类型有四种: 值类型 引用类型 输出类型 数组型参数 值参数: 所谓值参数,就是利用值向方法传递参数时,编译程序给实参的值做一份拷贝,并将此拷贝传递给该方法,这样做的结果就是被调用 ...

  3. python函数参数类型及其顺序

    根据inspect模块官文文档中关于函数参数类型的相关说明,python函数参数共有五种类型,按顺序分别为:POSITIONAL_ONLY.POSITIONAL_OR_KEYWORD.VAR_POSI ...

  4. <经验杂谈>C#中一种最简单、最基本的反射(Reflection):通过反射获取方法函数

    说起反射之前和很多用C#/.net的同仁们一样,相比于一般应用层对数据的增删改查总有点觉得深奥到难以理解.其实程序这东西,用过.实践过就很简单,我一直这么认为. 先说下概念:反射 Reflection ...

  5. javaSE_05Java中方法(函数)与重载、递归

    1.方法的声明和调用 什么是方法?为什么需要方法?代码复用,方便软件升级 什么是方法? 具备特定功能的一段独立的代码段 标准的方法格式:(注意格式的顺序) 修饰符 返回值类型 方法名(参数类型 参数名 ...

  6. JavaScript中判断整字类型最简洁的实现方法

    这篇文章主要介绍了JavaScript中判断整字类型最简洁的实现方法,本文给出多个判断整数的方法,最后总结出一个最短.最简洁的实现方法,需要的朋友可以参考下 我们知道JavaScript提供了type ...

  7. 浅谈MFC类CrackMe中消息处理函数查找方法

    最近一个学姐发给我了一份CrackMe希望我解一下,其中涉及到了MFC的消息函数查找的问题,就顺便以此为例谈一下自己使用的消息函数查找的方法.本人萌新,如果有任何错漏与解释不清的地方,欢迎各路大佬指正 ...

  8. C#中的方法(函数),委托和事件

    1. 先有方法,然后再有委托,最后再有事件 2. 思考能不能将 A方法 作为参数,传递给 B方法呢? eg: 在传统的编程方式中,A方法 调用 B方法的方式如下: //洗菜 public void W ...

  9. jquery ajax中支持哪些返回类型以及js中判断一个类型常用的方法?

    1 jquery ajax中支持哪些返回类型在JQuery中,AJAX有三种实现方式:$.ajax() , $.post , $.get(). 预期服务器返回的数据类型.如果不指定,jQuery 将自 ...

随机推荐

  1. day17--JQuery

        JQuery(中文社区:http://jquery.cuishifeng.cn/) 模块  类库  在不同语言叫法不一样而已 DOM/BOM/JavaScript的类库 一.查找元素 JQue ...

  2. C#连接oracle连接字符串

    /// <summary> /// Oracle 的数据库连接字符串. /// </summary> private const String connString = @&q ...

  3. Linux dnsmasq.conf

    一.配置文件:局域网内使用此dns服务时候首先会在host.dnsmasp里面找对应域名,若找不到则在resolv.dnsmasq中找 [root@operation_server dnsmasq.d ...

  4. Ubuntu (虚拟机同样) 更换内核?

    1:查看当前安装的内核dpkg -l|grep linux-image 2:查看可以更新的内核版本:sudo apt-cache search linux-image 3:安装新内核sudo apt- ...

  5. hdu 2844 Coins【多重背包】

    题目链接:https://vjudge.net/contest/228640#problem/F 转载于:http://www.voidcn.com/article/p-mxcorksq-gh.htm ...

  6. Python Django 学习 (一) 【Django 框架初探】

    1. 简介: Python下有许多款不同的 Web 框架.Django是重量级选手中最有代表性的一位.2008年9月发布第一个版本,目前的Django版本应该是2.1. 2. 本文的环境 OS : W ...

  7. LVN与其在Linux上的实现

    参考资料: LVM详解-骏马金龙-博客园 How to reduce the size of an LVM partition formatted with xfs filesystem on Cen ...

  8. BZOJ.4031.[HEOI2015]小Z的房间(Matrix Tree定理 辗转相除)

    题目链接 辗转相除解行列式的具体实现? 行列式的基本性质. //864kb 64ms //裸的Matrix Tree定理.练习一下用辗转相除解行列式.(因为模数不是质数,所以不能直接乘逆元来高斯消元. ...

  9. BZOJ.3546.[ONTAK2010]Life of the Party(二分图匹配 ISAP)

    题目链接 题意:求哪些点一定在最大匹配中. 这儿写过,再写一遍吧. 求哪些点不一定在最大匹配中.首先求一遍最大匹配,未匹配点当然不一定在最大匹配中. 设一个未匹配点为A,如果存在边A-B,且存在匹配边 ...

  10. Python图形编程探索系列-07-程序登录界面设计

    设计任务 初步设计程序登录界面,详细分析设计步骤. 程序详细分析 基本框架设计 import tkinter as tk import tkinter.messagebox root = tk.Tk( ...