函数参数 3个月前

编程语言
633
函数参数

当函数接受参数时,就必须指定以下内容:

  • 函数在其定义中指定接受的参数列表,以及这些参数的类型。
  • 在每个函数调用中提供匹配的实参列表。

仔细阅读 C#规范会发现行參( parameter )和实参( argument )之间存在一些细微的区别:行參是函数定义的一部分,而实参则由调用代码传递给函数。但是,这两个术语通常被简单地称为参数,似乎没有人对此感到十分不满。

示例代码如下所示,其中可以有任意数量的参数,每个参数都有一个类型和一个名称:

static <returnType> <FunctionName>(<paramType> <paramName>, ...)
{
    ...
        return <returnValue>;
}

参数用逗号分隔开。每个参数都在函数的代码中用作一个变量。例如,下面是一个简单的函数,带有两个 double 参数,并返回它们的乘积:

static double Product(double param1, double param2)
{
    return param1 * parma2;
}

下面看一个较复杂的示例。 把下列代码添加到 Program.cs 中:

class Program
{
    static int MaxValue(int[] intArray)
    {
        int maxVal = intArray[0];
        for (int i = 1; i < intArray.Length; i++)
        {
            if (intArray[i] > maxVal)
                maxVal = intArray[i];
        }
        return maxVal;
    }

> static void Main(string[] args)
{
    int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };
    int maxVal = MaxValue(myArray);
    Console.WriteLine("The maximum value in myArray is {0}", maxVal);
    Console.ReadKey();
}
}

示例的说明 这段代码包含一个函数,它执行的任务就是本章引言中示例函数所完成的任务。该函数以一个整数数组作为参数,并返回该数组中的最大值。该函数的定义如下所示:

static int MaxValue(int[] intArray)
{
    int maxVal = intArray[0];
    for (int i = 1; i < intArray.Length; i++)
    {
        if (intArray[i] > maxVal)
            maxVal = intArray[i];
    }
    return maxVal;
}

函数 MaxValue() 定义了一个参数,即 int 数组 intArray,它还有一个 int 类型的返回值。最大值的计算是很简单的。局部整型变量 maxVal 初始化为数组中的第一个值,然后把这个值与数组中后面的每个元素依次进行比较。如果一个元素的值比 maxVal 大,就用这个值代替当前的 maxVal 值。循环结束时,maxVal 就包含数组中的最大值,用 return 语句返回。 Main() 中的代码声明并初始化一个简单的整数数组,用于 MaxValue() 函数:

int[] myArray = { 1, 8, 3, 6, 2, 5, 9, 3, 0, 2 };

调用 MaxValue(),把一个值赋给 int 变量 maxVal

int maxVal = MaxValue(myArray);

接着,使用 Console.WriteLine() 把这个值写到屏幕上:

Console.WriteLine("The maximum value in myArray is {0}", maxVal);

参数匹配

在调用函数时,必须使提供的参数与函数定义中指定的参数完全匹配,这意味着要匹配参数的类型、个数和顺序。例如,下面的函数:

static void MyFunction(string myString, double myDouble)
{
    ...
}

不能使用下面的代码调用:

MyFunction(2.6, "Hello");  ❌

这里试图把一个 double 值作为第一个参数传递,把 string 值作为第二个参数传递,参数顺序与函数声明中定义的顺序不匹配。

也不能使用下面的代码:

MyFunction("Hello");  ❌

这里仅传送一个 string 参数,而该函数需要两个参数。使用上述两个函数调用都会产生编译错误,因为编译器要求必须匹配所有函数的签名。 使用函数的名称和参数来定义函数的签名。

再回顾这个示例,MaxValue() 只能用于获取整数数组中的最大 int 值。如果用下面的代码替换 Main() 中的代码:

static void Main(string[] args)
{
    double[] myArray = { 1.3, 8.9, 3.3, 6.5, 2.7, 5.3 };
    double maxVal = MaxValue(myArray);  ❌
        Console.WriteLine("The maximum value in myArray is {0}", maxVal);
    Console.ReadKey();
}

就不能编译这段代码,因为参数类型是错误的。在本章后面的 “重载函数” 一节将介绍解决这个问题的一个有效技术。


参数数组

C#允许为函数指定一个(只能指定一个)特殊参数,这个参数必须是函数定义中的最后一个参数,称为参数数组。参数数组允许用个数不定的参数调用函数,可使用 params 关键字定义它们。

参数数组可以简化代码,因为在调用代码中不必传递数组,而是传递同类型的几个参数,这些参数会被放在可在函数中使用的一个数组中。

定义使用参数数组的函数时,需要使用下列代码:

static <returnType> <FunctionName>(<p1Type> <p1Name>, ..., params <type>[] <name>)
{
    ...
        return <returnValue>;
}

使用下面的代码可以调用该函数。

<FunctionName>(<p1>, ..., <val1>, <val2>, ...)

其中 <val1 > <val2 > 等都是 <type > 类型的值,用于初始化 <name > 数组。可以指定的参数个数几乎不受限制,但它们都必须是 <type > 类型。甚至根本不必指定参数。

这一点使参数数组特别适合于函数提供一些额外的信息,供它们在处理过程中使用。例如,假定有一个函数 GetWord(),它的第一个参数是一个 string 值,并返回字符串中的第一个单词。

string firstWord = GetWord("This is a sentence.");

其中 firstWord 被赋予字符串 This

可在 GetWord() 中添加一个 params 参数,以根据其索引选择另一个要返回的单词:

string firstWord = GetWord("This is a sentence.", 2);

假定第一个单词计数为 1,则 firstWord 就被赋予字符串 is

也可以在第 3 个参数中限制返回的字符个数,同样通过 params 参数来实现:

string firstWord = GetWord("This is a sentence.", 4, 3);

此时 firstWord 被赋予字符串 sen

下面的示例定义并使用带有 params 类型参数的函数。 把下述代码添加到 Program.cs 中:

class Program
{
    static int SumVals(params int[] vals)
    {
        int sum = 0;
        foreach(int val in vals)
        {
            sum += val;
        }
        return sum;
    }

    static void Main(string[] args)
    {
        int sum = SumVals(1, 5, 2, 9, 8);
        Console.WriteLine("Summed Values = {0}", sum);
        Console.ReadKey();
    }
}

示例的说明 这个示例用关键字 params 定义函数 sumVals(),该函数可以接受任意个 int 参数(但不接受其他类型的参数):

static int SumVals(params int[] vals) { ... }

这个函数对 vals 数组中的值进行迭代,将这些值加在一起,返回其结果。 在 Main() 中,用 5 个整型参数调用函数 SumVals()

int sum = SumVals(1, 5, 2, 9, 8);

也可以用 0、1、2 或 100 个整型参数调用这个函数--参数的数量不受限制。 引入指定函数参数的新方式,包括用一种可读性更好的方式来包含可选参数。以后将介绍这些方法,从该章中可以了解到自问世以来,C#语言发生了怎样的变迁。


引用参数和值参数

本章迄今定义的所有函数都带有值参数。其含义是,在使用参数时,是把一个值传递给函数使用的一个变量。对函数中此变量的任何修改都不影响函数调用中指定的参数。例如,下面的函数使传递过来的参数值加倍,并显示出来:

static void ShowDouble(int val)
{
    val *= 2;
    Console.WriteLine("val doubled = {0}", val);
}

参数 val 在这个函数中被加倍,如果按一下方式调用它:

int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
ShowDouble(myNumber);
Console.WriteLine("myNumber = {0}", myNumber);

输出到控制台上的文本如下所示:

myNumber = 5
val doubled = 10

myNumber = 5

myNumber 作为一个参数,调用 ShowDouble() 并不影响 Main()myNumber 大的值,即使把 myNumber 赋值给 val 后将 val 加倍,myNumber 的值也不变。

这很不错,但如果要改变 myNumber 的值,就会有问题。可以使用一个为 myNumber 返回新值的函数:

static int DoubleNum(int val)
{
    val *= 2;
    return val;
}

并使用下面的代码调用它:

int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
myNumber = DoubleNum(myNumber);
Console.WriteLine("myNumber = {0}", myNumber);

但这段代码一点也不直观,且不能改变用作参数的多个变量值(因为函数只有一个返回值)。

此时可以通过 “引用”传递参数。即函数处理的变量与函数调用中使用的变量相同,而不仅仅是值相同的变量。因此,对这个变量进行的任何改变都会影响用作参数的变量值。为此,只需使用 ref 关键字指定参数:

static void ShowDouble(ref int val)
{
    val *= 2;
    Console.WriteLine("val doubled = {0}", val);
}

在函数调用中再次指定它(这是必须的,因为 ref 参数是函数签名的一部分):

int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
ShowDouble(ref myNumber);
Console.WriteLine("myNumber = {0}", myNumber);

输出到控制台的文本如下所示:

myNumber = 5
val doubled = 10
myNumber = 10

这次,myNumberShowDouble() 修改了。

用作 ref 参数的变量有两个限制。首先,函数可能会改变引用参数的值,所以必须在函数调用中使用 “非常量” 变量。所以,下面的代码是非法的:

const int myNumber = 5;
Console.WriteLine("myNumber = {0}", myNumber);
ShowDouble(ref myNumber);
Console.WriteLine("myNumber = {0}", myNumber);

其次,必须使用初始化过的变量。C#不允许假定 ref 参数在使用它的函数中初始化,下面的代码也是非法的:

int myNumber; ShowDouble(ref myNumber); ❌
Console.WriteLine("myNumber = {0}", myNumber);

输出参数

除了按引用传递值外,还可以使用 out 关键字,指定所给的参数是一个输出参数。 out 关键字的使用方式与 ref 关键字相同(在函数定义和函数调用中用作参数的修饰符)。实际上,它的执行方式与引用参数完全一样,因为在函数执行完毕后,该参数的值将返回给函数调用中使用的变量。但是,二者存在一些重要区别:

  • 把未赋值的变量用作 ref 参数是非法的,但可以把未赋值的变量用作 out 参数。* 另外,在函数使用 out 参数时,必须把它看成是尚未赋值。

    即调用代码可以把已赋值的变量用作 out 参数,但存储在该变量中的值会在函数执行时丢失。

    例如,考虑前面返回数组中最大值的 MaxValue() 函数,略微修改该函数,获取数组中最大值的元素索引。为简单起见,如果数组中有多个元素的值都是这个最大值,只提取第一个最大值的索引。为此,修改函数,添加一个 out 参数,如下所示:

static int MaxValue(int[] intArray, out int maxIndex)
{
    int maxVal = intArray[0];
    maxIndex = 0;
    for (int i = 1; i < intArray.Length; i++)
    {
        if (intArray[i] > maxVal)
        {
            maxVal = intArray[i];
            maxIndex = i;
        }
    }
    return maxVal;
}

可以采用以下方式使用该函数:

int[] myArray = { 1, 8, 3, 6, 2, 5 9, 3, 0, 2 };
int maxIndex;
Console.WriteLine("The maximum value in myArray is {0}", MaxValue(myArray, out maxIndex));
Console.WriteLine("The first occurrence of this value is at element {0}", maxIndex + 1);

结果如下:

The maximum value in myArray is 9
The first occurrence of this value is at element 7

注意 ⚠️,必须在函数调用中使用 out 关键字,就像 ref 关键字一样。

**在屏幕上显示结果时,给返回的 maxIndex 的值加上 1。这样可以使索引更容易读懂,因此数组的第一个元素记为元素 1,而不是元素 0。 * *

image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2222752
累计阅读

热门教程文档

Python
76小节
C
14小节
Linux
51小节
Lua
21小节
CSS
33小节