[你必须知道的.NET]第十一回:参数之惑---传递的艺术(上)

2024-01-02 00:58

本文主要是介绍[你必须知道的.NET]第十一回:参数之惑---传递的艺术(上),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文将介绍以下内容:

  • 按值传递与按引用传递深论
  • ref和out比较 
  • 参数应用浅析 

 

1. 引言

接上回《第九回:品味类型---值类型与引用类型(中)-规则无边》中,对值类型和引用类型的讨论,其中关于string类型的参数传递示例和解释,引起园友的关注和讨论,可谓一石激起千层浪。受教于装配脑袋的深切指正,对这一概念有了相当进一步的了解,事实证明是我错了,在此向朋友们致歉,同时非常感谢大家的参与,尤其是装配脑袋的不倦相告。

因此,本文就以更为清晰的角度,把我理解有误的雷区作做以深入的讨论与分析,希望通过我的一点点努力和探讨至少对如下几个问题能有清晰的概念:

  • 什么是按值传递?什么是按引用传递?
  • 按引用传递和按引用类型参数传递的区别?
  • ref与out在按引用传递中的比较与应用如何?
  • param修饰符在参数传递中的作用是什么?

2. 参数基础论

简单的来说,参数实现了不同方法间的数据传递,也就是信息交换。Thinking in Java的作者有过一句名言:一切皆为对象。在.NET语言中也是如此,一切数据都最终抽象于类中封装,因此参数一般用于方法间的数据传递。例如典型的Main入口函数就有一个string数组参数,args是函数命令行参数。通常参数按照调用方式可以分为:形参和实参。形参就是被调用方法的参数,而实参就是调用方法的参数。例如: 

using System;

public class Arguments
{
  
public static void Main(string [] args)
  {
    
string myString = "This is your argument.";
    
//myString是实际参数
    ShowString(myString);
  }

  
private void ShowString(string astr)
  {
    Console.WriteLine(astr);
  }
}


由上例可以得出以下几个关于参数的基本语法:

  1. 形参和实参必须类型、个数与顺序对应匹配;
  2. 参数可以为空;
  3. 解析Main(string [] args),Main函数的参数可以为空,也可以为string数组类,其作用是接受命令行参数,例如在命令行下运行程序时,args提供了输入命令行参数的入口。
  4. 另外,值得一提的是,虽然CLR支持参数默认值,但是C#中却不能设置参数默认值,这一点让我很郁闷,不知为何?不过可以通过重载来变相实现,具体如下:
static void JudgeKind(string name, string kind)
{
    Console.WriteLine(
"{0} is a {1}", name, kind);
}


static void JudgeKind(string name)
{
    
//伪代码
    if(name is person)
    {
        Console.WriteLine(name, 
"People");
    }
}

 这种方法可以扩展,可以实现更多个默认参数实现,不过,说实话有些多此一举,不够灵活,不爽不爽。 

3. 传递的基础

接下来,我们接上面的示例讨论,重点将参数传递的基础做以交代,以便对参数之惑有一个从简入繁的演化过程。我们以基本概念的形式来一一列出这些基本概念,先混个脸儿熟,关于形参、实参、参数默认值的概念就不多做交代,参数传递是本文的核心内容,将在后文以大量的笔墨来阐述。所以接下来的概念,我们就做以简单的引入不花大量的精力来讨论,主要包括: 

3.1 泛型类型参数

泛型类型参数,可以是静态的,例如MyGeneric<int>;也可以是动态的,此时它其实就是一个占位符,例如MyGeneric<T>中的T可以是任何类型的变量,在运行期动态替换为相应的类型参数。泛型类型参数一般也以T开头来命名。 

3.2 可变数目参数

一般来说参数个数都是固定的,定义为集群类型的参数可以实现可变数目参数的目的,但是.NET提供了更灵活的机制来实现可变数目参数,这就是使用param修饰符。可变数目参数的好处就是在某些情况下可以方便的提供对于参数个数不确定情况的实现,例如计算任意数字的加权和,连接任意字符串为一个字符串等。我们以一个简单的示例来展开对这个问题的论述,为:


在此基础上,我们将使用param关键字实现可变数目参数的规则和使用做以小结为:

  • param关键字的实质是:param是定制特性ParamArrayAttribute的缩写(关于定制特性的详细论述请参见第三回:历史纠葛:特性和属性),该特性用于指示编译器的执行过程大概可以简化为:编译器检查到方法调用时,首先调用不包含ParamArrayAttribute特性的方法,如果存在这种方法就施行调用,如果不存在才调用包含ParamArrayAttribute特性的方法,同时应用方法中的元素来填充一个数组,同时将该数组作为参数传入调用的方法体。总之就是param就是提示编译器实现对参数进行数组封装,将可变数目的控制由编译器来完成,我们可以很方便的从上述示例中得到启示。例如:

    static void ShowAgeSum(string team, params int[] ages){...}

实质上是这样子:

    static void ShowAgeSum(string team, [ParamArrayAttribute] int[] ages){...}

  • param修饰的参数必须为一维数组,事实上通常就是以群集方式来实现多个或者任意多个参数的控制的,所以数组是最简单的选择;
  • param修饰的参数数组,可是是任何类型。因此,如果需要接受任何类型的参数时,只要设置数组类型为object即可;
  • param必须在参数列表的最后一个,并且只能使用一次。  

4. 深入讨论,传递的艺术 

默认情况下,CRL中的方法都是按值传递的,但是在具体情况会根据传递的参数情况的不同而有不同的表现,我们在深入讨论传递艺术的要求下,就是将不同的传递情况和不同的表现情况做以小结,从中剥离出参数传递复杂表现之内的实质所在。从而为开篇的几个问题给出清晰的答案。

4.1 值类型参数的按值传递

首先,参数传递根据参数类型分为按值传递和按引用传递,默认情况下都是按值传递的。按值传递主要包括值类型参数的按值传递和引用类型参数的按值传递。值类型实例传递的是该值类型实例的一个拷贝,因此被调用方法操作的是属于自己本身的实例拷贝,因此不影响原来调用方法中的实例值。以例为证: 

// FileName    : Anytao.net.My_Must_net
// Description : The .NET what you should know of arguments. 
// Release      : 2007/07/01 1.0

// Copyright   : (C)2007 Anytao.com  http://www.anytao.com

using System;

namespace Anytao.net.My_Must_net
{
    
class Args
    {
        
public static void Main()
        {
            
int a = 10;
            Add(a);
            Console.WriteLine(a);
        }

        
private static void Add(int i)
        {
            i 
= i + 10;
            Console.WriteLine(i);
        }
    }
}


休息一下,请看下篇『第十二回:参数之惑---传递的艺术(下) 

 

参考文献

(USA)Jeffrey Richter, Applied Microsoft .NET Framework Programming

(USA)David Chappell, Understanding .NET

 

温故知新

[开篇有益]
[第一回:恩怨情仇:is和as]
[第二回:对抽象编程:接口和抽象类]
[第三回:历史纠葛:特性和属性]
[第四回:后来居上:class和struct]
[第五回:深入浅出关键字---把new说透]
[第六回:深入浅出关键字---base和this]
[第七回:品味类型---从通用类型系统开始]
[第八回:品味类型---值类型与引用类型(上)-内存有理]
[第九回:品味类型---值类型与引用类型(中)-规则无边]
[第十回:品味类型---值类型与引用类型(下)-应用征途]

这篇关于[你必须知道的.NET]第十一回:参数之惑---传递的艺术(上)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/560949

相关文章

Spring Boot spring-boot-maven-plugin 参数配置详解(最新推荐)

《SpringBootspring-boot-maven-plugin参数配置详解(最新推荐)》文章介绍了SpringBootMaven插件的5个核心目标(repackage、run、start... 目录一 spring-boot-maven-plugin 插件的5个Goals二 应用场景1 重新打包应用

Python中你不知道的gzip高级用法分享

《Python中你不知道的gzip高级用法分享》在当今大数据时代,数据存储和传输成本已成为每个开发者必须考虑的问题,Python内置的gzip模块提供了一种简单高效的解决方案,下面小编就来和大家详细讲... 目录前言:为什么数据压缩如此重要1. gzip 模块基础介绍2. 基本压缩与解压缩操作2.1 压缩文

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

一文详解PostgreSQL复制参数

《一文详解PostgreSQL复制参数》PostgreSQL作为一款功能强大的开源关系型数据库,其复制功能对于构建高可用性系统至关重要,本文给大家详细介绍了PostgreSQL的复制参数,需要的朋友可... 目录一、复制参数基础概念二、核心复制参数深度解析1. max_wal_seChina编程nders:WAL

Linux高并发场景下的网络参数调优实战指南

《Linux高并发场景下的网络参数调优实战指南》在高并发网络服务场景中,Linux内核的默认网络参数往往无法满足需求,导致性能瓶颈、连接超时甚至服务崩溃,本文基于真实案例分析,从参数解读、问题诊断到优... 目录一、问题背景:当并发连接遇上性能瓶颈1.1 案例环境1.2 初始参数分析二、深度诊断:连接状态与

使用easy connect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题

《使用easyconnect之后,maven无法使用,原来需要配置-Djava.net.preferIPv4Stack=true问题》:本文主要介绍使用easyconnect之后,maven无法... 目录使用easGWowCy connect之后,maven无法使用,原来需要配置-DJava.net.pr

史上最全nginx详细参数配置

《史上最全nginx详细参数配置》Nginx是一个轻量级高性能的HTTP和反向代理服务器,同时也是一个通用代理服务器(TCP/UDP/IMAP/POP3/SMTP),最初由俄罗斯人IgorSyso... 目录基本命令默认配置搭建站点根据文件类型设置过期时间禁止文件缓存防盗链静态文件压缩指定定错误页面跨域问题

在.NET平台使用C#为PDF添加各种类型的表单域的方法

《在.NET平台使用C#为PDF添加各种类型的表单域的方法》在日常办公系统开发中,涉及PDF处理相关的开发时,生成可填写的PDF表单是一种常见需求,与静态PDF不同,带有**表单域的文档支持用户直接在... 目录引言使用 PdfTextBoxField 添加文本输入域使用 PdfComboBoxField