黑马程序员-console.writeline,不定长参数以及params

本文深入探讨C#中params参数的使用方法与内部机制,通过对比C语言的变长参数处理方式,解析object类型在C#中的实现细节。

------- Windows Phone 7手机开发.Net培训、期待与您交流! -------

console.writeline()有一个重载是这样的:

public static void WriteLine(string format, params object[] arg);

百度params,进一步得出答案:

arams 构造函数声明数组 而不知道数组长度 用的
在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。

形如:
using System;
public class MyClass 
{
    public static void UseParams(params object[] list) 
    {
        for (int i = 0 ; i < list.Length; i++)
        {
            Console.WriteLine(list[i]);
        }
        Console.WriteLine();
    }

params参数允许数组在运行时确定长度

也就是说wirteline函数这个重载把N个参数都放进一个数组中,不过数组的长度是多少,然后根据format中占位符的数量把数组中的参数读取出来

然而问题又来了,后面的数组中的数据类型是不同的,程序怎么知道要读取的变量在内存中有多长呢?

答案是显然的,函数把所有的变量都打包成了object类型,而object类型是所有类型的子类

再继续深挖下去,object类型是固定长度的吗?于是我们的新问题就变成了,object类型在底层的具体实现是什么样的?


笔者先不给出答案,笔者想到C语言中函数的一种写法:

void func(char *str,...);

这个...不是我故意省略了,而是真的可以写成"..."(必须是3个点)

这代表编译器不检查第一个参数后面参数的类型和个数,而读取参数的工作交给程序员来完成

具体读取的过程大概是这样的:

声明一个字符指针(因为C语言中字符指针的偏移量是1个字节)指向字符串str的末尾(也就是后面一个参数的开始)

按字符读取str串,比如这个串是“dcs”,读取到第一个字符d

这时进入一个switch循环中:

 ...

case d:

printf("%d",p);//读取一个整形变量那么长然后输出,假设我们这个函数的实现是输出不定长个变量

p+=sizeof(int);//p是刚才那个字符指针,偏移4个字节,指向下一个变量的开始

break;

...

然后读取第二个字符c,c表示读取一个char类型,指针偏移 加1个字节,指向第三个变量

第三个是s,表示读取一个字符串,指针一直加1直到遇见"\0"结束标记

如果前面串中还有字符,就依次类推。

这种读取方式基于一个假定的事实,所有的参数在内存中都是连续储存的,如果不连续,就无法确定偏移量是多少

我们再回到C#中来,思考一下object类型:

笔者推测出,object类型实际上封装了一个指针,和一个说明参数类型的字节(也可能是多个字节,但总之这个说明占用的长度是固定的)

指针的大小也是固定的4个字节,所以所有的object都有统一大小,而由其生成子类型的方式,其实是根据指针指向了内存中那个子类型变量的区域

那么你们还能提出更多问题吗,比如拆箱和装箱?留给你们去思考吧,嘿嘿

### C# 中 `Console.WriteLine` 的使用方法 #### 基本语法 `Console.WriteLine` 是用于向控制台输出文本并自动换行的方法。其基本语法如下: ```csharp Console.WriteLine(string value); ``` 此方法可以接受多种类型的参数,不仅限于字符串。 --- #### 输出简单字符串 最简单的用法是直接传递一个字符串给 `Console.WriteLine` 方法[^2]。 ```csharp Console.WriteLine("这是一个测试"); // 输出结果:这是一个测试 ``` --- #### 输出其他数据类型 除了字符串外,还可以输出其他数据类型的对象。对于非字符串对象,`Console.WriteLine` 会调用该对象的 `ToString()` 方法来获取其文本表示形式[^3]。 ```csharp int number = 42; double pi = 3.14159; bool isTrue = true; Console.WriteLine(number); // 输出结果:42 Console.WriteLine(pi); // 输出结果:3.14159 Console.WriteLine(isTrue); // 输出结果:True ``` --- #### 格式化输出 通过占位符 `{}` 可以实现更复杂的格式化输出。可以在大括号中指定索引位置以及对齐方式和格式说明符[^4]。 ```csharp string name = "Alice"; int age = 25; decimal salary = 5000.75m; Console.WriteLine("姓名: {0}, 年龄: {1}, 工资: {2:C}", name, age, salary); // 输出结果:姓名: Alice, 年龄: 25, 工资: ¥5,000.75 ``` 上述例子中,`{0}` 表示第一个参数的位置,依次类推。`:C` 则指定了货币格式。 --- #### 对齐与填充 可以通过设置对齐参数调整输出内容的排列方向和宽度。正数值表示右对齐,负数值表示左对齐。 ```csharp Console.WriteLine("{0,-10} | {1,10}", "名称", "数量"); Console.WriteLine("{0,-10} | {1,10}", "苹果", 10); Console.WriteLine("{0,-10} | {1,10}", "香蕉", 20); /* 输出结果: 名称 | 数量 苹果 | 10 香蕉 | 20 */ ``` 在此例中,`-10` 和 `10` 分别设置了列宽并对齐方式。 --- #### 处理异常情况 当遇到输入/输出错误时,可能会抛出 `IOException` 异常。因此,在实际开发过程中应考虑捕获此类异常。 ```csharp try { Console.WriteLine("尝试写入..."); } catch (System.IO.IOException ex) { Console.Error.WriteLine($"发生 IO 错误: {ex.Message}"); } ``` --- #### 完整示例程序 下面展示了一个综合使用的完整代码片段: ```csharp using System; namespace ConsoleAppExample { class Program { static void Main() { string productName = "Laptop"; int quantity = 3; decimal pricePerUnit = 1200.50m; Console.WriteLine("产品详情:"); Console.WriteLine("-------------------"); Console.WriteLine($"名称: {productName}"); Console.WriteLine($"数量: {quantity}"); Console.WriteLine($"单价: {pricePerUnit:C}"); Console.WriteLine(); Console.WriteLine("表格视图:"); Console.WriteLine($"{ "名称",-10} | { "数量",5} | {"价格",8}"); Console.WriteLine(new string('-', 25)); Console.WriteLine($"{ productName,-10} | { quantity,5} | { pricePerUnit,8:C}"); } } } /* 输出结果: 产品详情: ------------------- 名称: Laptop 数量: 3 单价: ¥1,200.50 表格视图: 名称 | 数量 | 价格 ------------------------- Laptop | 3 | ¥1,200.50 */ ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值