C# 编程_第02章_基础语法
C# 封装
封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。
抽象允许相关信息可视化,封装则使程序员实现所需级别的抽象。
封装使用 访问修饰符 来实现。
一个 访问修饰符 定义了一个类成员的范围和可见性。
C# 支持的访问修饰符如下所示:
- Public
- Protected
- Private
- Internal
- Protected internal
Public 访问修饰符允许一个类将其成员变量和成员函数暴露给其他的函数和对象。
任何公有成员可以被外部的类访问。
下面的实例说明了这点:
using System;
namespace RectangleApplication
{
class Rectangle
{
//成员变量
public double length;
public double width;
public double GetArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("长度: {0}", length);
Console.WriteLine("宽度: {0}", width);
Console.WriteLine("面积: {0}", GetArea());
}
}//end class Rectangle
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.length = 6;
r.width = 7;
r.Display();
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
长度: 6
宽度: 7
面积: 42
在上面的实例中,成员变量 length 和 width 被声明为 public,所以它们可以被函数 Main() 使用 Rectangle 类的实例 r 访问。
成员函数 Display() 和 GetArea() 也可以不通过类的实例直接访问这些变量。
成员函数 Display() 也被声明为 public,所以它也能被 Main() 使用 Rectangle 类的实例 r 访问。
Private 访问修饰符允许一个类将其成员变量和成员函数对其他的函数和对象进行隐藏。
只有同一个类中的函数可以访问它的私有成员。
即使是类的实例也不能访问它的私有成员。
下面的实例说明了这点
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace RectangleApplication{
class Rectangle{
// 私有方法
private double length;
private double width;
private double GetArea(){
return length * width;
}
// 公有方法
public void AcceptDetails(){
Console.WriteLine("请输入长度:");
length = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入宽度:");
width = Convert.ToDouble(Console.ReadLine());
}
public void Display(){
Console.WriteLine("长度:{0}",length);
Console.WriteLine("宽度:{0}",width);
Console.WriteLine("面积:{0}",GetArea());
}
}
class ExecuteRectangle{
static void Main(string[] args){
Rectangle rect = new Rectangle();
rect.AcceptDetails();
rect.Display();
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
请输入长度:
6
请输入宽度:
7
长度: 6
宽度: 7
面积: 42
在上面的实例中,成员变量 length 和 width 被声明为 private,所以它们不能被函数 Main() 访问。
成员函数 AcceptDetails() 和 Display() 可以访问这些变量。
由于成员函数 AcceptDetails() 和 Display() 被声明为 public,所以它们可以被 Main() 使用 Rectangle 类的实例 r 访问。
Protected 访问修饰符允许子类访问它的基类的成员变量和成员函数。这样有助于实现继承。暂且不表
Internal 访问说明符 允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。其他应用程序则不能访问
换句话说,带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问。
这儿牵扯到一个程序引用另一个程序的dll文件问题!
下面的实例说明了这点:
using System;
namespace RectangleApplication
{
class Rectangle
{
//成员变量
internal double length;
internal double width;
// 默认是private
double GetArea()
{
return length * width;
}
public void Display()
{
Console.WriteLine("长度: {0}", length);
Console.WriteLine("宽度: {0}", width);
Console.WriteLine("面积: {0}", GetArea());
}
}//end class Rectangle
class ExecuteRectangle
{
static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.length = 6;
r.width = 7;
r.Display();
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
长度: 6
宽度: 7
面积: 42
在上面的实例中,请注意成员函数 GetArea() 声明的时候不带有任何访问修饰符。
如果没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private。
Protected Internal 访问修饰符允许一个类将其成员变量和成员函数 对同一应用程序内的子类以外的其他的类对象和函数进行隐藏。这也被用于实现继承。???Excuse Me???
补充说明:
被 internal 修饰的东西只能在本程序集(当前项目)内被使用。其他项目中无论如何也不能使用!
被 protected internal 修饰的属性/方法 可以在其他项目中,被派生类使用
举例代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace LoveHelper{
// 只能在本namespace内部,才可被访问
internal class Girl{
// 公有方法
public void ShowDetails(){
Console.WriteLine("展示女孩的细节");
}
}
// 程序内的媒婆(老鸨)
public class MeiPo{
// 介绍女孩子
public void makeIntroduce(){
Girl cuteGirl = new Girl();
cuteGirl.ShowDetails();
}
}
}
// 假设,下面是另一个程序集
using LoveHelper;
namespace Business{
class Visitor{
static void Main(string[] args){
// 可以让媒婆,介绍女生;但不能让女生自己介绍
LoveHelper.MeiPo meipo = new LoveHelper.MeiPo();
meipo.makeIntroduce();
// 下面就是错误的做法,因为另一个程序,是不能访问导入的dll中的被internal修饰的类 class Girl不允许在其他程序集中访问
LoveHelper.Girl cuteGirl = new LoveHelper.Girl();
cuteGirl.ShowDetails();
}
}
}
C# 方法
一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。每一个 C# 程序至少有一个带有 Main 方法的类。
要使用一个方法,您需要:
当定义一个方法时,从根本上说是在声明它的结构的元素。在 C# 中,定义方法的语法如下:
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{
Method Body
}
下面是方法的各个元素:
- Access Specifier:访问修饰符,这个决定了变量或方法对于另一个类的可见性。
- Return type:返回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为 void。
- Method name:方法名称,是一个唯一的标识符,且是大小写敏感的。它不能与类中声明的其他标识符相同。C#中一般首字母大写
- Parameter list:参数列表,使用圆括号括起来,该参数是用来传递和接收方法的数据。参数列表是指方法的参数类型、顺序和个数。参数是可选的,也就是说,一个方法可能不包含参数。
- Method body:方法主体,包含了完成任务所需的指令集。
下面的代码片段显示一个函数 FindMax,它接受两个整数值,并返回两个中的较大值。它有 public 访问修饰符,所以它可以使用类的实例从类的外部进行访问。
class NumberManipulator
{
public int FindMax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
...
}
您可以使用方法名调用方法。下面的实例演示了这点:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public int FindMax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
static void Main(string[] args)
{
/* 局部变量定义 */
int a = 6;
int b = 7;
int ret;
NumberManipulator n = new NumberManipulator();
//调用 FindMax 方法
ret = n.FindMax(a, b);
Console.WriteLine("最大值是: {0}", ret );
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
最大值是: 7
您也可以使用类的实例从另一个类中调用其他类的公有方法。
例如,方法 FindMax 属于 NumberManipulator类,您可以从另一个类 Test 中调用它。
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public int FindMax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
}
class Test
{
static void Main(string[] args)
{
/* 局部变量定义 */
int a = 6;
int b = 7;
int ret;
NumberManipulator n = new NumberManipulator();
//调用 FindMax 方法
ret = n.FindMax(a, b);
Console.WriteLine("最大值是: {0}", ret );
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
最大值是: 7
一个方法可以自我调用。这就是所谓的 递归。下面的实例使用递归函数计算一个数的阶乘:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public int factorial(int num)
{
/* 局部变量定义 */
int result;
if (num == 1)
{
return 1;
}
else
{
result = factorial(num - 1) * num;
return result;
}
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
//调用 factorial 方法
Console.WriteLine("6 的阶乘是: {0}", n.factorial(6));
Console.WriteLine("7 的阶乘是: {0}", n.factorial(7));
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
6 的阶乘是: 720
7 的阶乘是: 5040
当调用带有参数的方法时,您需要向方法传递参数。在 C# 中,有三种向方法传递参数的方式:
方式 | 描述 |
---|
值参数 | 这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。 |
引用参数 | 这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。 |
输出参数 | 这种方式可以返回多个值。 |
按值传递参数
这是参数传递的默认方式。在这种方式下,当调用一个方法时,会为每个值参数创建一个新的存储位置。
实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。下面的实例演示了这个概念:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 6;
int b = 7;
Console.WriteLine("在交换之前,a 的值: {0}", a);
Console.WriteLine("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(a, b);
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在交换之前,a 的值:6
在交换之前,b 的值:7
在交换之后,a 的值:6
在交换之后,b 的值:7
结果表明,按值传递,即使在函数内改变了值,值也没有发生任何的变化。
按引用传递参数
引用参数是一个对变量的内存位置的引用。当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。
在 C# 中,使用 ref 关键字声明引用参数。下面的实例演示了这点:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{ // 参数要加 ref
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 6;
int b = 7;
Console.WriteLine("在交换之前,a 的值: {0}", a);
Console.WriteLine("在交换之前,b 的值: {0}", b);
/* 参数要加ref 调用函数来交换值 */
n.swap(ref a, ref b);
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在交换之前,a 的值:6
在交换之前,b 的值:7
在交换之后,a 的值:6
在交换之后,b 的值:7
结果表明,swap 函数内的值改变了,且这个改变可以在 Main 函数中反映出来。
按输出传递参数
return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数关键字out 来从函数中返回两个值。
输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
下面的实例演示了这点:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{ // 注意:这儿有一个out关键字
public void getValue(out int x )
{
int temp = 520;
x = temp;
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 67;
Console.WriteLine("在方法调用之前,a 的值: {0}", a);
/* 注意这儿有一个out关键字! 调用函数来获取值 */
n.getValue(out a);
Console.WriteLine("在方法调用之后,a 的值: {0}", a);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在方法调用之前,a 的值: 67
在方法调用之后,a 的值: 520
提供给输出参数的变量不需要赋值。
当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用。
请看下面的实例,来理解这一点:
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void getValues(out int x, out int y )
{
Console.WriteLine("请输入第一个值: ");
x = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("请输入第二个值: ");
y = Convert.ToInt32(Console.ReadLine());
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 1314, b = 520;
/* 调用函数来获取值 */
n.getValues(out a, out b);
Console.WriteLine("在方法调用之后,a 的值: {0}", a);
Console.WriteLine("在方法调用之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果(取决于用户输入):
请输入第一个值:
6
请输入第二个值:
7
在方法调用之后,a 的值: 6
在方法调用之后,b 的值: 7
C# 可空类型(Nullable ?)
C# 提供了一个特殊的数据类型,nullable 类型(可空类型),
可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。
例如,Nullable< Int32 >,读作"可空的 Int32",可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可以被赋值为 null 值。
类似的,Nullable< bool > 变量可以被赋值为 true 或 false 或 null。
在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。
例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义。
声明一个 nullable 类型(可空类型)的语法如下:
< data_type> ? <variable_name> = null;
下面的实例演示了可空数据类型的用法:
using System;
namespace CalculatorApplication
{
class NullablesAtShow
{
static void Main(string[] args)
{
int? num1 = null;
int? num2 = 45;
double? num3 = new double?();
double? num4 = 3.14157;
bool? boolval = new bool?();
// 显示值
Console.WriteLine("显示可空类型的值: {0}, {1}, {2}, {3}",
num1, num2, num3, num4);
Console.WriteLine("一个可空的布尔值: {0}", boolval);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
显示可空类型的值: , 45, , 3.14157
一个可空的布尔值:
代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class NullableShow{
// 注意 类型后面的问号 ?
static void Main(string[] args)
{
int? a = null;
int? b = 67;
// 注意 类型后面的问号 ?
double? c = new double?();
double? d = 3.1415926;
// 注意 类型后面的问号 ?
bool? e = new bool?();
Console.WriteLine("a的值是{0},b的值是{1},c的值是{2},d的值是{3},e的值是{4}",a,b,c,d,e);
Console.ReadLine();
}
}
}
运行效果如下:
Null 合并运算符用于定义可空类型 和 引用类型的默认值。
Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。
Null 合并运算符把操作数类型 隐式转换 为另一个可空(或不可空)的值类型的操作数的类型。??????
如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。
下面的实例演示了这点:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class NullableShow{
// 注意 类型后面的问号 ?
static void Main(string[] args)
{
double? a = null;
double? b = 13.14;
double c;
c = a ?? 520; // a如果为null,则使用后面的值
Console.WriteLine("c = {0}",c);
c = b ?? 67; // b如果为null,则使用后面的值
Console.WriteLine("c = {0}",c);
Console.ReadLine();
}
}
}
运行效果如下:
C# 数组(Array)
数组是一个存储相同类型元素的固定大小的顺序集合。
数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合。
声明数组变量并不是声明 number0、number1、...、number99 一个个单独的变量,
而是声明一个就像 numbers 这样的变量,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来表示一个个单独的变量。
数组中某个指定的元素是通过索引来访问的。
所有的数组都是由连续的内存位置组成的。
最低的地址对应第一个元素,最高的地址对应最后一个元素。
在 C# 中声明一个数组,您可以使用下面的语法:
datatype[] arrayName;
其中,
- datatype 用于指定被存储在数组中的元素的类型。
- [ ] 指定数组的秩(维度)。秩指定数组的大小。
- arrayName 指定数组的名称。
例如:
double[] balance;
在C#中, 声明一个数组不会在内存中初始化数组。
当初始化数组变量时,您可以赋值给数组。
数组是一个引用类型,所以您需要使用 new 关键字来创建数组的实例。
例如:
double[] balance = new double[10];
您可以通过使用索引号赋值给一个单独的数组元素,比如:
double[] balance = new double[10];
balance[0] = 4500.0;
您可以在声明数组的同时给数组赋值,比如:
double[] balance = { 2340.0, 4523.69, 3421.0};
您也可以通过new关键字,创建的同时 初始化一个数组,比如:
int [] marks = new int[5] { 99, 98, 92, 97, 95};
在上述情况下,你也可以省略数组的大小,比如:
int [] marks = new int[] { 99, 98, 92, 97, 95};
您也可以赋值一个数组变量到另一个目标数组变量中。
在这种情况下,目标和源会指向相同的内存位置:
int [] marks = new int[] { 99, 98, 92, 97, 95};
int[] score = marks;
当您创建一个数组时,C# 编译器会根据数组类型隐式初始化每个数组元素为一个默认值。
例如,int 数组的所有元素都会被初始化为 0。
元素是通过带索引的数组名称来访问的。
这是通过把元素的索引放置在数组名称后的方括号中来实现的。例如:
double salary = balance[9];
下面是一个实例,使用上面提到的三个概念,即声明、赋值、访问数组:
using System;
namespace ArrayApplication
{
class MyArray
{
static void Main(string[] args)
{
int [] n = new int[3]; /* n 是一个带有 3 个整数的数组 */
int i,j;
/* 初始化数组 n 中的元素 */
for ( i = 0; i < n.Length; i++ )
{
n[ i ] = i + 5;
}
/* 输出每个数组元素的值 */
for (j = 0; j < n.Length; j++ )
{
Console.WriteLine("Element[{0}] = {1}", j, n[j]);
}
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
Element[0] = 5
Element[1] = 6
Element[2] = 7
在前面的实例中,我们使用一个 for 循环来访问每个数组元素。
您也可以使用一个 foreach 语句来遍历数组. 代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class ArrayShow{
// 注意 类型后面的问号 ?
static void Main(string[] args)
{
int[] arr = new int[3];
for(int i = 0;i < arr.Length;i++){
arr[i] = i + 5;
}
foreach(int j in arr){
int i = j - 5;
Console.WriteLine("arr[{0}] = {1}",i,j);
}
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
arr[0] = 5
arr[1] = 6
arr[2] = 7
在 C# 中,数组是非常重要的,且需要了解更多的细节。下面列出了 C# 程序员必须清楚的一些与数组相关的重要概念:
概念 | 描述 |
---|
多维数组 | C# 支持多维数组。多维数组最简单的形式是二维数组。 |
交错数组 | C# 支持交错数组,即数组的数组。 |
传递数组给函数 | 您可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。 |
参数数组 | 这通常用于传递未知数量的参数给函数。如Main(string[] args) |
Array 类 | 在 System 命名空间中定义,是所有数组的基类,并提供了各种用于数组的属性和方法。 |
C# 多维数组
C# 支持多维数组。多维数组又称为矩形数组。
您可以声明一个 string 变量的二维数组,如下:
注意:中间用的是逗号
string [,] names;
或者,您可以声明一个 int 变量的三维数组,如下:
int [ , , ] arr3D;
多维数组最简单的形式是二维数组。
一个二维数组,在本质上,是一个一维数组的列表。
一个二维数组可以被认为是一个带有 x 行和 y 列的表格。下面是一个二维数组,包含 n 行和 m 列:
因此,数组中的每个元素是使用形式为 a[ i , j ] 的元素名称来标识的,其中 a 是数组名称,i 和 j 是唯一标识 a 中每个元素的下标。
初始化二维数组
多维数组可以通过在括号内为每行指定值来进行初始化。下面是一个带有 3 行 4 列的数组。
// 使用new 关键字
int [,] arr = new int [3,4] {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
访问二维数组元素
二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的。例如:
// 注意:这儿还是用逗号分隔的
int val = a[2,3];
上面的语句将获取数组中第 3 行第 4 个元素。
让我们来看看下面的程序,我们将使用嵌套循环来处理二维数组
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class ArrayShow{
// 注意 类型后面的问号 ?
static void Main(string[] args)
{
// 3行 2列
int [,] arr = new int[3,2]{
{67,520},
{13,14},
{2006,2010}
};
// 矩阵的秩
int zhi = arr.Rank;
// 元素的个数
int totalElementCount = arr.Length;
Console.WriteLine("totalElementCount:{0}",totalElementCount);
// 行数
int row = arr.GetLength(0);
Console.WriteLine("row:{0}",row);
// 列数
int coloumn = arr.GetLength(1);
for(int i = 0; i < row ; i++){
for(int j = 0; j < coloumn; j++){
Console.WriteLine("arr[{0}][{1}] = {2}",i,j,arr[i,j]);
}
}
Console.ReadLine();
}
}
}
上面的代码被编译和执行时,它会产生下列结果
C# 交错数组
交错数组是数组的数组。您可以声明一个带有 int 值的交错数组 scores,如下所示:
int [][] scores;
声明一个数组不会在内存中创建数组。创建上面的数组:
int[][] scores = new int[5][];
for (int i = 0; i < scores.Length; i++)
{
scores[i] = new int[4];
}
您可以初始化一个交错数组,如下所示:
int[][] scores = new int[2][]{new int[]{92,93,94},new int[]{85,66,87,88}};
其中,scores 是一个由两个整型数组组成的数组 -- scores[0] 是一个带有 3 个整数的数组,scores[1] 是一个带有 4 个整数的数组。
代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class ArrayShow{
// 注意 类型后面的问号 ?
static void Main(string[] args)
{
int[][] arr = new int[5][];
Console.WriteLine(arr.Length);
for(int i = 0; i < arr.Length;i++){
arr[i] = new int[4];
Console.WriteLine(arr.Length);
}
Console.ReadLine();
}
}
}
运行效果:
下面的实例演示了如何使用交错数组:
using System;
namespace ArrayApplication
{
class MyArray
{
static void Main(string[] args)
{
/* 一个由 5 个整型数组组成的交错数组 */
int[][] a = new int[][]{new int[]{0,0},new int[]{1,2},
new int[]{2,4},new int[]{ 3, 6 }, new int[]{ 4, 8 } };
int i, j;
/* 输出数组中每个元素的值 */
for (i = 0; i < 5; i++)
{
for (j = 0; j < 2; j++)
{
Console.WriteLine("a[{0}][{1}] = {2}", i, j, a[i][j]);
}
}
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a[0][0]: 0
a[0][1]: 0
a[1][0]: 1
a[1][1]: 2
a[2][0]: 2
a[2][1]: 4
a[3][0]: 3
a[3][1]: 6
a[4][0]: 4
a[4][1]: 8
代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class ArrayShow{
static void Main(string[] args)
{
// 交错数组的定义
int[][] arr = new int[][]{
new int[]{6,7},
new int[]{5,2,0},
new int[]{13,14}
};
// 注意:C#方法名首字母一般大写
for(int i = 0;i < arr.GetLength(0);i++){
for(int j = 0;j < arr[i].GetLength(0);j++){
Console.WriteLine("a[{0}][{1}] = {2}",i,j,arr[i][j]);
}
}
Console.ReadLine();
}
}
}
运行效果如下:
C# 传递数组给函数
在 C# 中,您可以传递数组作为函数的参数。
您可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。
下面的实例演示了如何传递数组给函数:
代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class ArrayShow{
double GetAverage(int[] arr,int count){
int sum = 0;
double average = 0;
for(int i = 0;i < count ;i++){
sum += arr[i];
}
average = (double) sum / count;
return average;
}
static void Main(string[] args)
{
ArrayShow arrayShow = new ArrayShow();
int[] arr = new int[]{67,520,13,14,2006};
double average = arrayShow.GetAverage(arr,arr.Length);
Console.WriteLine("平均值:{0}",average);
Console.ReadKey();
}
}
}
运行效果:
C# 参数数组
有时,当声明一个方法时,您不能确定要传递给函数作为参数的参数数目。
C# 参数数组解决了这个问题,参数数组通常用于传递未知数量的参数给函数。
在使用数组作为形参时,C# 提供了 params 关键字,
使调用数组为形参的方法时,既可以传递数组实参,也可以只传递一组数组。
params 的使用格式为:
public 返回类型 方法名称( params 类型名称[] 数组名称 )
下面的实例演示了如何使用参数数组:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class ParamArray{
// 参数数目不确定
public int AddElements(params int[] arr){
int sum = 0;
foreach(int i in arr){
sum += i;
}
return sum;
}
}
class TestClass{
static void Main(string[] args)
{
ParamArray paramArray = new ParamArray();
// 参数数目不确定
int sum = paramArray.AddElements(6,7,520,13,14);
Console.WriteLine("总和:{0}",sum);
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果
C# Array 类
Array 类是 C# 中所有数组的基类,它是在 System 命名空间中定义。Array 类提供了各种用于数组的属性和方法。
下表列出了 Array 类中一些最常用的属性:
序号 | 属性 & 描述 |
---|
1 | IsFixedSize 获取一个值,该值指示数组是否带有固定大小。 |
2 | IsReadOnly 获取一个值,该值指示数组是否只读。 |
3 | Length 获取一个 32 位整数,该值表示所有维度的数组中的元素总数。 |
4 | LongLength 获取一个 64 位整数,该值表示所有维度的数组中的元素总数。 |
5 | Rank 获取数组的秩(维度)。 |
下表列出了 Array 类中一些最常用的方法:
序号 | 方法 & 描述 |
---|
1 | Clear 根据元素的类型,设置数组中某个范围的元素为零、 false 或者 null。 |
2 | Copy(Array, Array, Int32 rangeSize) 从数组的第一个元素开始复制某个范围的元素到另一个数组的第一个元素位置。长度由一个 32 位整数指定。 |
3 | CopyTo(Array, Int32) 从当前的一维数组中复制所有的元素到一个指定的一维数组的指定索引位置。索引由一个 32 位整数指定。 |
4 | GetLength 获取一个 32 位整数,该值表示指定维度的数组中的元素总数。 |
5 | GetLongLength 获取一个 64 位整数,该值表示指定维度的数组中的元素总数。 |
6 | GetLowerBound 获取数组中指定维度的下界。 |
7 | GetType 获取当前实例的类型。从对象(Object)继承。 |
8 | GetUpperBound 获取数组中指定维度的上界。 |
9 | GetValue(Int32) 获取一维数组中指定位置的值。索引由一个 32 位整数指定。 |
10 | IndexOf(Array, Object) 搜索指定的对象,返回整个一维数组中第一次出现的索引。 |
11 | Reverse(Array) 逆转整个一维数组中元素的顺序。 |
12 | SetValue(Object, Int32) 给一维数组中指定位置的元素设置值。索引由一个 32 位整数指定。 |
13 | Sort(Array) 使用数组的每个元素的 IComparable 实现来排序整个一维数组中的元素。 |
14 | ToString 返回一个表示当前对象的字符串。从对象(Object)继承。 |
下面的程序演示了 Array 类的一些方法的用法:
using System;
namespace ArrayApplication
{
class MyArray
{
static void Main(string[] args)
{
int[] list = { 34, 72, 13, 44, 25, 30, 10 };
int[] temp = list;
Console.Write("原始数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine();
// 逆转数组
Array.Reverse(temp);
Console.Write("逆转数组: ");
foreach (int i in temp)
{
Console.Write(i + " ");
}
Console.WriteLine();
// 排序数组
Array.Sort(list);
Console.Write("排序数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine();
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
原始数组: 34 72 13 44 25 30 10
逆转数组: 10 30 25 44 13 72 34
排序数组: 10 13 25 30 34 44 72
代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class ArrayOperator{
static void Main(string[] args)
{
int[] arr = {6,7,520};
int[] tmpArr = arr;
Console.Write("原始数组:");
foreach(int i in arr){ //字符串串联运算符
Console.Write(i + " ");
}
Console.WriteLine();
// 逆转裁判
Array.Reverse(tmpArr);
Console.Write("逆转数组:");
foreach(int i in tmpArr){ // 字符串 串联运算符
Console.Write(i + " ");
}
Console.WriteLine();
// 排序数组
Array.Sort(tmpArr);
Console.Write("排序数组");
foreach(int i in tmpArr){ // 字符串 串联运算符
Console.Write(i + " ");
}
Console.WriteLine();
Console.ReadKey();
}
}
}
运行效果如下:
C# 字符串(String)
在 C# 中,您可以使用字符数组来表示字符串,
但是,更常见的做法是使用 string 关键字来声明一个字符串变量。
string 关键字是 System.String 类的别名。
您可以使用以下方法之一来创建 string 对象:
- 通过给 String 变量指定一个字符串
- 通过使用 String 类构造函数
- 通过使用字符串 串联运算符( + )
- 通过检索属性或调用一个返回字符串的方法
- 通过格式化方法来转换一个值或对象为它的字符串表示形式
下面的实例演示了这点:
代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class StringOperator{
static void Main(string[] args)
{
// 通过 + 号
string part_1,part_2;
part_1 = "あの";
part_2 = "花";
string animeName = part_1 + part_2;
Console.WriteLine("anime name is:{0}",animeName);
// 通过string 构造函数
char[] alphaArr = {'a','n','o','h','a','n','a'};
string animeName2 = new string(alphaArr);
Console.WriteLine("anime2 name is:{0}",animeName2);
// 通过方法返回
string[] wordArr = {"我们","仍未知道","那年夏天","所见到的","花的名字"};
string animeName3 = String.Join(" ",wordArr);
Console.WriteLine("anime3 name is:{0}",animeName3);
// 通过类型转化
DateTime lastTime = new DateTime(2006,6,7,5,20,0);
string timeStr = String.Format("Last Msg Sent At {0:t} On {0:D}",lastTime);
Console.WriteLine("Last Msg:{0}",timeStr);
Console.ReadKey();
}
}
}
运行效果:
String 类有以下两个属性:
序号 | 属性名称 & 描述 |
---|
1 | Chars[Int32] 在当前 String 对象中获取 Char 对象的指定位置的对象。 |
2 | Length 在当前的 String 对象中获取字符数。 |
String 类有许多方法用于 string 对象的操作。下面的表格提供了一些最常用的方法:
序号 | 方法名称 & 描述 |
---|
1 | public static int Compare( string strA, string strB ) 比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。该方法区分大小写。 |
2 | public static int Compare( string strA, string strB, bool ignoreCase ) 比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。但是,如果布尔参数为真时,该方法不区分大小写。 |
3 | public static string Concat( string str0, string str1 ) 连接两个 string 对象。 |
4 | public static string Concat( string str0, string str1, string str2 ) 连接三个 string 对象。>_< |
5 | public static string Concat( string str0, string str1, string str2, string str3 ) 连接四个 string 对象。>_< |
6 | public bool Contains( string value ) 返回一个表示指定 string 对象是否出现在字符串中的值。 |
7 | public static string Copy( string str ) 创建一个与指定字符串具有相同值的新的 String 对象。 |
8 | public void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count ) 从 string 对象的指定位置开始复制指定数量的字符到 Unicode 字符数组中的指定位置。 |
9 | public bool EndsWith( string value ) 判断 string 对象的结尾是否匹配指定的字符串。 |
10 | public bool Equals( string value ) 判断当前的 string 对象是否与指定的 string 对象具有相同的值。 |
11 | public static bool Equals( string a, string b ) 判断两个指定的 string 对象是否具有相同的值。 |
12 | public static string Format( string format, Object arg0 ) 把指定字符串中一个或多个格式项替换为指定对象的字符串表示形式。 |
13 | public int IndexOf( char value ) 返回指定 Unicode 字符在当前字符串中第一次出现的索引,索引从 0 开始。 |
14 | public int IndexOf( string value ) 返回指定字符串在该实例中第一次出现的索引,索引从 0 开始。 |
15 | public int IndexOf( char value, int startIndex ) 返回指定 Unicode 字符从该字符串中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。 |
16 | public int IndexOf( string value, int startIndex ) 返回指定字符串从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。 |
17 | public int IndexOfAny( char[] anyOf ) 返回某一个指定的 Unicode 字符数组中任意字符在该实例中第一次出现的索引,索引从 0 开始。 |
18 | public int IndexOfAny( char[] anyOf, int startIndex ) 返回某一个指定的 Unicode 字符数组中任意字符从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。 |
19 | public string Insert( int startIndex, string value ) 返回一个新的字符串,其中,指定的字符串被插入在当前 string 对象的指定索引位置。 |
20 | public static bool IsNullOrEmpty( string value ) 指示指定的字符串是否为 null 或者是否为一个空的字符串。 |
21 | public static string Join( string separator, params string[] value ) 连接一个字符串数组中的所有元素,使用指定的分隔符分隔每个元素。 |
22 | public static string Join( string separator, string[] value, int startIndex, int count ) 链接一个字符串数组中的指定元素,使用指定的分隔符分隔每个元素。 |
23 | public int LastIndexOf( char value ) 返回指定 Unicode 字符在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。 |
24 | public int LastIndexOf( string value ) 返回指定字符串在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。 |
25 | public string Remove( int startIndex ) 移除当前实例中的所有字符,从指定位置开始,一直到最后一个位置为止,并返回字符串。 |
26 | public string Remove( int startIndex, int count ) 从当前字符串的指定位置开始移除指定数量的字符,并返回字符串。 |
27 | public string Replace( char oldChar, char newChar ) 把当前 string 对象中,所有指定的 Unicode 字符替换为另一个指定的 Unicode 字符,并返回新的字符串。 |
28 | public string Replace( string oldValue, string newValue ) 把当前 string 对象中,所有指定的字符串替换为另一个指定的字符串,并返回新的字符串。 |
29 | public string[] Split( params char[] separator ) 返回一个字符串数组,包含当前的 string 对象中的子字符串,子字符串是使用指定的 Unicode 字符数组中的元素进行分隔的。 |
30 | public string[] Split( char[] separator, int count ) 返回一个字符串数组,包含当前的 string 对象中的子字符串,子字符串是使用指定的 Unicode 字符数组中的元素进行分隔的。int 参数指定要返回的子字符串的最大数目。 |
31 | public bool StartsWith( string value ) 判断字符串实例的开头是否匹配指定的字符串。 |
32 | public char[] ToCharArray() 返回一个带有当前 string 对象中所有字符的 Unicode 字符数组。 |
33 | public char[] ToCharArray( int startIndex, int length ) 返回一个带有当前 string 对象中所有字符的 Unicode 字符数组,从指定的索引开始,直到指定的长度为止。 |
34 | public string ToLower() 把字符串转换为小写并返回。 |
35 | public string ToUpper() 把字符串转换为大写并返回。 |
36 | public string Trim() 移除当前 String 对象中的所有前导空白字符和后置空白字符。 |
上面的方法列表并不详尽,请访问 MSDN 库,查看完整的方法列表和 String 类构造函数。
下面的实例演示了上面提到的一些方法:
比较字符串
using System;
namespace StringApplication
{
class StringProg
{
static void Main(string[] args)
{
string str1 = "a no ha na";
string str2 = "a no ha na";
if (String.Compare(str1, str2) == 0)
{
Console.WriteLine(str1 + " and " + str2 + " are equal.");
}
else
{
Console.WriteLine(str1 + " and " + str2 + " are not equal.");
}
Console.ReadKey() ;
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
This is test and This is text are not equal.
字符串包含字符串:
using System;
namespace StringApplication
{
class StringProg
{
static void Main(string[] args)
{
string str = "This is test";
if (str.Contains("test"))
{
Console.WriteLine("The sequence 'test' was found.");
}
Console.ReadKey() ;
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
The sequence 'test' was found.
获取子字符串:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class StringOperator{
static void Main(string[] args)
{
string str1 = "a no hana";
// 从下标5开始往后面截取
string subStr2 = str1.Substring(5);
Console.WriteLine(str1);
Console.WriteLine("from index 5:{0}",subStr2);
Console.ReadKey();
}
}
}
连接字符串,代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
class StringOperator{
static void Main(string[] args)
{
string[] strArr = new string[]{
"Winter is coming",
"凛冬已至",
"未闻花名",
"龙与虎"
};
string newStr = String.Join("\n",strArr);
Console.WriteLine(newStr);
Console.ReadKey();
}
}
}
运行效果如下:
C# 结构(Struct)
在 C# 中,结构是值类型数据结构。
它使得一个单一变量可以存储各种数据类型的相关数据。
struct 关键字用于创建结构。
结构是用来代表一个记录。假设您想跟踪图书馆中书的动态。您可能想跟踪每本书的以下属性:
- Title
- Author
- Subject
- Book ID
为了定义一个结构,您必须使用 struct 语句。struct 语句为程序定义了一个带有多个成员的新的数据类型。
例如,您可以按照如下的方式声明 Book 结构:
struct Girl
{
public string name;
public int age;
};
下面的程序演示了结构的用法
代码如下:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
//定义结构
struct Girl
{
public string name;
public int age;
};
class StructOperator{
static void Main(string[] args)
{
// 面码
Girl menma;
// 逢坂大河
Girl tiger;
menma.name = "面码";
menma.age = 15;
tiger.name = "逢坂大河";
tiger.age = 16;
Console.WriteLine("{0} no age is:{1}",menma.name,menma.age);
Console.WriteLine("{0} no age is:{1}",tiger.name,tiger.age);
Console.ReadKey();
}
}
}
运行效果如下:
您已经用了一个名为 Girl 的结构。
在 C# 中的结构与传统的 C 或 C++ 中的结构不同。
C# 中的结构有一下特点:
- 结构可带有方法、字段、索引、属性、运算符方法和事件。
- 结构可定义构造函数,但不能定义析构函数。并且,您不能为结构定义默认的构造函数。默认的构造函数是自动定义的,且不能被改变。
- 与类不同,结构不能继承其他的结构或类。
- 结构不能作为其他结构或类的基础结构。
- 结构可实现一个或多个接口。
- 结构成员不能指定为 abstract、virtual 或 protected。
- 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。
- 与类不同,结构可以不使用 New 操作符即可被实例化。
- 如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。
类和结构有以下几个基本的不同点:
- 类是引用类型,结构是值类型。
- 结构不支持继承。
- 结构不能声明默认的构造函数。
针对上述讨论,让我们重写前面的实例:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace CalculatorApplication{
//定义结构
struct Girl
{
private string _name;
private int _age;
public void initGirl(string name,int age){
_name = name;
_age = age;
}
public void showGirl(){
Console.WriteLine("{0} no age is {1}",_name,_age);
}
};
class StructOperator{
static void Main(string[] args)
{
// 面码
Girl menma = new Girl();
// 逢坂大河
Girl tiger = new Girl();
menma.initGirl("面码",15);
tiger.initGirl("逢坂大河",16);
menma.showGirl();
tiger.showGirl();
Console.ReadKey();
}
}
}
运行效果如下:
C# 枚举(Enum)
枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
C# 枚举是值数据类型。换句话说,枚举包含自己的值,且不能继承或传递继承。
声明枚举的一般语法:
enum <enum_name>
{
enumeration list
};
其中,
- enum_name 指定枚举的类型名称。
- enumeration list 是一个用逗号分隔的标识符列表。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0.例如:
enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat };
下面的实例演示了枚举变量的用法:
// 注意是大写的
// 注意:C#里方法名首字母一般大写
using System;
namespace EnumApplication{
class EnumOperator{
//定义枚举,不能是字符串!
enum Girls{Monday = 6,Tuesday};
static void Main(string[] args)
{
// 必须强转
int mon = (int)Girls.Monday;
Console.WriteLine("xxx:{0}",mon);
int tue = (int)Girls.Tuesday;
Console.WriteLine("yyy:{0}",tue);
Console.ReadKey();
}
}
}
运行效果:
未完待续,下一章节,つづく