zoukankan      html  css  js  c++  java
  • 【C#语言规范版本5.0学习】1.5类和对象(二、类的方法)

    ➤方  法

    方法 (method) 是一种成员,用于实现可由对象或类执行的计算或操作。

    静态方法 (static method) 通过类来访问。

    实例方法 (instance method) 通过类的实例来访问。

    方法具有一个参数 (parameter) 列表(可以为空),表示传递给该方法的值或变量引用;

    方法还具有一个返回类型 (return type),指定该方法计算和返回的值的类型。如果方法不返回值,则其返回类型为 void。

    与类型一样,方法也可以有一组类型参数,当调用方法时必须为类型参数指定类型实参。与类型不同的是,类型实参经常可以从方法调用的实参推断出,而无需显式指定。

    方法的签名 (signature) 在声明该方法的类中必须唯一。方法的签名由方法的名称、类型参数的数目以及该方法的参数的数目、修饰符和类型组成方法的签名不包含返回类型

    ⟰ 参  数

    参数用于向方法传递值或变量引用。

    方法的参数从调用该方法时指定的实参 (argument) 获取它们的实际值。

    有四类参数:值参数、引用参数、输出参数和参数数组。

    值参数 (value parameter) 用于传递输入参数。一个值参数相当于一个局部变量,只是它的初始值来自为该形参传递的实参。对值参数的修改不影响为该形参传递的实参。 值参数可以是可选的,通过指定默认值可以省略对应的实参。

    引用参数 (reference parameter) 用于传递输入和输出参数。为引用参数传递的实参必须是变量,并且在方法执行期间,引用参数与实参变量表示同一存储位置。引用参数使用 ref 修饰符声明

    下面的示例演示 ref 参数的用法。

    using System;
    class Test
    {
    static void Swap(ref int x, ref int y) {
    int temp = x;
    x = y;
    y = temp;
    }
    static void Main() {
    int i = 1, j = 2;
    Swap(ref i, ref j);
    Console.WriteLine("{0} {1}", i, j); // Outputs "2 1"
    }
    }

    输出参数 (output parameter) 用于传递输出参数。对于输出参数来说,调用方提供的实参的初始值并不重要。除此之外,输出参数与引用参数类似。输出参数是用 out 修饰符声明的。

    下面的示例演示 out 参数的用法。

    using System;
    class Test
    {
    static void Divide(int x, int y, out int result, out int remainder) {
    result = x / y;
    remainder = x % y;
    }
    static void Main() {
    int res, rem;
    Divide(10, 3, out res, out rem);
    Console.WriteLine("{0} {1}", res, rem); // Outputs "3 1"
    }
    }

    参数数组 (parameter array) 允许向方法传递可变数量的实参。参数数组使用 params 修饰符声明。只有方法的最后一个参数才可以是参数数组,并且参数数组的类型必须是一维数组类型。

    System.Console 类的 Write 和 WriteLine 方法就是参数数组用法的很好示例。它们的声明如下。

    public class Console
    {
    public static void Write(string fmt, params object[] args) {...}
    public static void WriteLine(string fmt, params object[] args) {...}
    ...
    }

    在使用参数数组的方法中,参数数组的行为完全就像常规的数组类型参数。但是,在具有参数数组的方法的调用中,既可以传递参数数组类型的单个实参,也可以传递参数数组的元素类型的任意数目的实参 在后一种情况下,将自动创建一个数组实例,并使用给定的实参对它进行初始化。示例:

    Console.WriteLine("x={0} y={1} z={2}", x, y, z);
    //等价于以下语句:
    string s = "x={0} y={1} z={2}";
    object[] args = new object[3];
    args[0] = x;
    args[1] = y;
    args[2] = z;
    Console.WriteLine(s, args);

    方法体和局部变量

    方法体指定了在调用该方法时将执行的语句。 方法体可以声明仅用在该方法调用中的变量。这样的变量称为局部变量 (local variable)。局部变量声明指定了类型名称、变量名称,还可指定初始值。

    下面的示例声明一个初始值为零的局部变量 i 和一个没有初始值的变量 j。

    using System;
    class Squares
    {
    static void Main() {
    int i = 0;
    int j;
    while (i < 10) {
    j = i * i;
    Console.WriteLine("{0} x {0} = {1}", i, j);
    i = i + 1;
    }
    }
    }

    C# 要求在对局部变量明确赋值 (definitely assigned) 之后才能获取其值。例如,如果前面对 i 的声明中未包括初始值,则编译器将针对随后对 i 的使用报错,因为 i 在程序中的这些位置还没有明确赋值。

    方法可以使用 return 语句将控制返回到它的调用方。在返回 void 的方法中,return 语句不能指定表达式。在返回非 void 的方法中,return 语句必须含有一个计算返回值的表达式。

     静态方法和实例方法

    使用 static 修饰符声明的方法为静态方法 (static method)静态方法不对特定实例进行操作,并且只能直接访问静态成员

    不使用 static 修饰符声明的方法为实例方法 (instance method)。实例方法对特定实例进行操作,并且能够访问静态成员和实例成员。

    在调用实例方法的实例上,可以通过 this 显式地访问该实例。而在静态方法中引用 this 是错误的。

    下面的 Entity 类具有静态成员和实例成员。 

    class Entity
    {
    static int nextSerialNo;
    int serialNo;
    public Entity() {
    serialNo = nextSerialNo++;
    }
    public int GetSerialNo() {
    return serialNo;
    }
    public static int GetNextSerialNo() {
    return nextSerialNo;
    }
    public static void SetNextSerialNo(int value) {
    nextSerialNo = value;
    }
    }

     每个 Entity 实例都包含一个序号(我们假定这里省略了一些其他信息)。Entity 构造函数(类似于实例方法)使用下一个可用的序号来初始化新的实例。

    由于该构造函数是一个实例成员,它既可以访问 serialNo 实例字段,也可以访问 nextSerialNo 静态字段。

    GetNextSerialNo 和 SetNextSerialNo 静态方法可以访问 nextSerialNo 静态字段,但是如果直接访问 serialNo 实例字段就会产生错误。

    下面的示例演示 Entity 类的使用。

    using System;
    class Test
    {
    static void Main() {
    Entity.SetNextSerialNo(1000);
    Entity e1 = new Entity();
    Entity e2 = new Entity();
    Console.WriteLine(e1.GetSerialNo()); // Outputs "1000"
    Console.WriteLine(e2.GetSerialNo()); // Outputs "1001"
    Console.WriteLine(Entity.GetNextSerialNo()); // Outputs "1002"
    }
    }

    注意:SetNextSerialNo 和 GetNextSerialNo 静态方法是在类上调用的,而 GetSerialNo 实例方法是在该类的实例上调用的。

    ⟰ 虚方法、重写方法和抽象方法

    若一个实例方法的声明中含有 virtual 修饰符,则称该方法为虚方法

    若其中没有 virtual 修饰符, 则称该方法为非虚方法。

    在调用一个虚方法时,该调用所涉及的实例的运行时类型 (runtime type) 确定了要实际调用的方法实现。

    在非虚方法调用中,实例的编译时类型 (compile-time type) 负责做出此决定。

    虚方法可以在派生类中重写 (override)。当某个实例方法声明包括 override 修饰符时,该方法将重写所继承的具有相同签名的虚方法

    虚方法声明用于引入新方法,而重写方法声明则用于使现有的继承虚方法专用化(通过提供该方法的新实现)。

    抽象 (abstract) 方法是没有实现的虚方法。

    抽象方法使用 abstract 修饰符进行声明并且只允许出现在同样被声明为 abstract 的类中抽象方法必须在每个非抽象派生类中重写

    下面的示例声明一个抽象类 Expression,它表示一个表达式目录树节点;它有三个派生类 Constant、 VariableReference 和 Operation,它们分别实现了常量、变量引用和算术运算的表达式目录树节点。

    using System;
    using System.Collections;
    public abstract class Expression
    {
    public abstract double Evaluate(Hashtable vars);
    }
    public class Constant: Expression
    {
    double value;
    public Constant(double value) {
    this.value = value;
    }
    public override double Evaluate(Hashtable vars) {
    return value;
    }
    }
    public class VariableReference: Expression
    {
    string name;
    public VariableReference(string name) {
    this.name = name;
    }
    public override double Evaluate(Hashtable vars) {
    object value = vars[name];
    if (value == null) {
    throw new Exception("Unknown variable: " + name);
    }
    return Convert.ToDouble(value);
    }
    }
    
    public class Operation: Expression
    {
    Expression left;
    char op;
    Expression right;
    public Operation(Expression left, char op, Expression right) {
    this.left = left;
    this.op = op;
    this.right = right;
    }
    public override double Evaluate(Hashtable vars) {
    double x = left.Evaluate(vars);
    double y = right.Evaluate(vars);
    switch (op) {
    case '+': return x + y;
    case '-': return x - y;
    case '*': return x * y;
    case '/': return x / y;
    }
    throw new Exception("Unknown operator");
    }
    }
    View Code

    上面的四个类可用于为算术表达式建模。

    例如,使用这些类的实例,表达式 x + 3 可如下表示。

    Expression e = new Operation( new VariableReference("x"), '+', new Constant(3));

    代码中调用了 Expression 实例的 Evaluate 方法,以计算给定表达式的值,从而生成一个 double 值。 该方法接受一个包含变量名称(作为哈希表项的键)和值(作为项的值)的 Hashtable 作为实参。 Evaluate 方法是一个虚抽象方法,意味着非抽象派生类必须重写该方法以提供实际的实现。 Constant 的 Evaluate 实现只是返回所存储的常量。

    VariableReference 的实现在哈希表中查找变量名称,并返回产生的值。

    Operation 的实现先对左操作数和右操作数求值(通过递归调用它们的 Evaluate 方法),然后执行给定的算术运算。

    下面的程序使用 Expression 类,对于不同的 x 和 y 值,计算表达式 x * (y + 2) 的值。

    using System;
    using System.Collections;
    class Test
    {
    static void Main() {
    Expression e = new Operation(
    new VariableReference("x"),
    '*',
    new Operation(
    new VariableReference("y"),
    '+',
    new Constant(2)
    )
    );
    Hashtable vars = new Hashtable();
    vars["x"] = 3;
    vars["y"] = 5;
    Console.WriteLine(e.Evaluate(vars)); // Outputs "21"
    
    vars["x"] = 1.5;
    vars["y"] = 9;
    Console.WriteLine(e.Evaluate(vars)); // Outputs "16.5"
    }
    }
    View Code

     方法重载

    方法重载 (overloading) 允许同一类中的多个方法具有相同名称,条件是这些方法具有唯一的签名。在编译一个重载方法的调用时,编译器使用重载决策 (overload resolution) 确定要调用的特定方法。重载决策将查找与参数最佳匹配的方法,如果没有找到任何最佳匹配的方法则报告错误信息。

    下面的示例演示重载决策的工作机制。Main 方法中的每个调用的注释表明实际调用的方法。 

    class Test
    {
    static void F() {
    Console.WriteLine("F()");
    }
    static void F(object x) {
    Console.WriteLine("F(object)");
    }
    static void F(int x) {
    Console.WriteLine("F(int)");
    }
    static void F(double x) {
    Console.WriteLine("F(double)");
    }
    static void F<T>(T x) {
    Console.WriteLine("F<T>(T)");
    }
    static void F(double x, double y) {
    Console.WriteLine("F(double, double)");
    }
    static void Main() {
    F(); // Invokes F()
    F(1); // Invokes F(int)
    F(1.0); // Invokes F(double)
    F("abc"); // Invokes F(object)
    F((double)1); // Invokes F(double)
    F((object)1); // Invokes F(object)
    F<int>(1); // Invokes F<T>(T)
    F(1, 1); // Invokes F(double, double) 
    }
    }

     正如该示例所示,总是通过显式地将实参强制转换为确切的参数类型和/或显式地提供类型实参,来选择一个特定的方法。

  • 相关阅读:
    关于二进制的利用
    2017年浙江中医药大学程序设计竞赛 Solution
    2018-2019 ACM-ICPC, Asia Xuzhou Regional Contest Solution
    2018-2019 ACM-ICPC, Asia Shenyang Regional Contest Solution
    2018-2019 ACM-ICPC, Asia Nanjing Regional Contest Solution
    AtCoder Grand Contest 029 Solution
    BZOJ 3307: 雨天的尾巴
    Codeforces Round #526 (Div. 2) Solution
    2016-2017 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) Solution
    [HZNUOJ] 博
  • 原文地址:https://www.cnblogs.com/TechSingularity/p/14326918.html
Copyright © 2011-2022 走看看