集合和数组:同一类型的多个对象
元组(Tuple):不同类型的多个对象
数组:同一类型的多个对象
1.声明了数组后,就必须为数组分配内存,以保存数组的所有元素
2. 数组是引用类型,所以必须给它分配堆上的内存,应使用new,指定数组中元素的的类型和数量来初始化数组的变量
3. 如果事先不知道数组中应包含多少个元素,应使用集合
4. 数组声明中的方括号必须跟在数据类型后面,且不能放在变量名称之后
5. 数组[]中初始值设定的数目必须与数组大小完全匹配
6. 在声明时没有初始化数组,则数组成员将自动初始化为该数组类型的默认初始值。另外,如果将数组声明为某类型的字段,则当实例化该类型时它将被设置为默认值 null
//声明数组
int[] myArray;
string[] strArray;
double[] douArray = new double[3];
//float[6],{必须有6个数,不然会报错}
float[] floArray = new float[6] { 3, 4, 7,6,9,6 };
int[] Array = { 3, 7, 8, 9 };
//数组初始化
myArray = new int[4];
//string[]不指定数组大小,编译器会自动统计{}中的元素个数,这里是2,相当于string[2]
strArray = new string[]{ "apple", "blue" };
1. 数组只支持有整型参数的索引器,索引器总是从0开头,表示第一个元素
2. 数组的索引是myArray.Length - 1(数组长度减一,因为数组从0开始)
//myArray.Length:数组的长度
for (int i = 0; i < myArray.Length; i++)
{
Console.WriteLine(myArray[i]);
}
如果数组中的元素是引用类型,就必须为每个数组元素分配内存。若使用了未分配内存的元素,就会抛出异常
//声明一个包含两个PhoneCustomer元素的数组(与声明一个int数组类似)
PhoneCustomer[] myphone = new PhoneCustomer[2];
//使用从0开始的索引器,可以为数组的每个元素分配内存
myphone[0] = new PhoneCustomer { Age = 1, FirstName = "apple" };
myphone[1] = new PhoneCustomer { Age = 2, FirstName = "berry" };
PhoneCustomer[] myphonetwo =
{
new PhoneCustomer{Age = 3, FirstName = "apple"},
new PhoneCustomer{Age = 4, FirstName = "apple"},
};
1. 多维数组用两个或多个整数来索引
2. 声明数组后,就不能修改其阶数了
3. 使用数组初始化器时,必须初始化数组的每个元素,不能遗漏任何元素
4. 不建议使用ArrayList,当数组里的元素是值类型在操作的时候会出现大量的装箱与拆箱步骤性能会损失许多,而是应该用什么类型的元素创建什么类型的数组,除非你的元素有交叉或不确定才考虑采用ArrayList
//声明一个二维数组,对应一个矩形
int[,] twodim = new int[3, 3];
//使用两个整数0,0作为索引器来访问数组中的元素
twodim[0, 0] = 1;
//事先知道元素的值,就可以使用数组索引器来初始化二维数组
int[,] twodimtow =
{
{1,2,3 },
{4,5,6 } ,
{7,8,9 }
};
//花括号中使用两个逗号,就可以声明三维数组
int[,,] threedim =
{
{{1,2},{3,4} },
{{5,6},{7,8} },
{{9,10}, { 11,12} }
};
//a的值是11,2是第三行,1是第二个括号,0是第二个括号里的第一个元素
var a = threedim[2, 1, 0];
1. 锯齿数组中,每一行都可以有不同的大小
2. 在初始化锯齿数组时,只有在第一对方括号中设置该数组包含的行数,定义各行中元素个数的第二个方括号设置为空,因为这类数组的每一行包含不同的元素个数
//new int[3][]中,第一个方括号设置数组包含的行数,这里是三行;第二个方括号为空,留着用户自己设置每一行的元素个数
int[][] jagged = new int[3][];
//jagged[0],jagged[1],jagged[1]是在设置new int[3][]中的第二个方括号的值,也就是每行的元素值
//第一行2个元素,第二行5个,第三行3个
jagged[0] = new int[2] { 1, 2 };
jagged[1] = new int[5] { 3, 4, 5, 6, 7 };
jagged[1] = new int[3] { 8, 9, 0 };
1. Array是一个抽象类,所以不能用构造函数来创建数组
2. 事先不知道元素的类型,可以用CreateInstance()创建数组
//CreateInstance方法第一个参数typeof(int)是元素的类型,第二个参数定义数组的大小
Array intArray = Array.CreateInstance(typeof(int), 5);
for(int i=0; i < 5; i++)
{
//SetValue方法设置对应元素的值,intArray中5个元素都是33
intArray.SetValue(33, i);
}
for (int i = 0; i < 5; i++)
{
//GetValue方法读取对应元素的值,i = 0,就读取intArray[0]的值
Console.WriteLine(intArray.GetValue(i));
}
//将已将创建的intArray强制转换成声明为int[]的数组
int[] intArray2 = (int[])intArray;
//CreateInstance方法有很多重载版本
//可以创建多维数组和不基于0的数组
//创建一个2×3个元素的二维数组
//lengths表示这是一个2×3个元素的二维数组,两行三列
int[] lengths = { 2, 3 };
//维度的下限:1,10,不从0开始
int[] lowerBounds = { 1, 10 };
Array arrac = Array.CreateInstance(typeof(PhoneCustomer), lengths, lowerBounds);
//SetValue()方法设置数组的元素,其参数是每一维的索引
arrac.SetValue(new PhoneCustomer
{
FirstName="apple",
Age = 18
},1,10);
arrac.SetValue(new PhoneCustomer
{
FirstName = "apple",
Age = 18
}, 1, 11);
arrac.SetValue(new PhoneCustomer
{
FirstName = "apple",
Age = 18
}, 1, 12);
arrac.SetValue(new PhoneCustomer
{
FirstName = "apple",
Age = 18
}, 2, 10);
PhoneCustomer[,] arrac2 = (PhoneCustomer[,])arrac;
PhoneCustomer phone1 = arrac2[2, 10];
数组是引用类型,如果一个数组变量赋予另一个数组变量,就会得到两个引用同一数组的变量。复制数组,会使数组实现ICloneable接口,这个接口定义的Clone()方法会创建数组的浅表副本。
1. 数组的元素是值类型,Clone()会复制所有的值
int[] Array1 = { 1, 2 };
int[] Array2 = (int[])Array1.Clone();
2. 数组包含引用,不复制元素,只复制引用
PhoneCustomer[] myphonetwo =
{
new PhoneCustomer{Age = 3, FirstName = "apple"},
new PhoneCustomer{Age = 4, FirstName = "apple"},
};
PhoneCustomer[] myphoneone = (PhoneCustomer[])myphonetwo.Clone();
3. 除了Clone(),还可以使用Array.Copy()。但是,Clone()会创建一个新数组,而Copy()方法必须传递阶数相同且有足够元素的已有数组
1. Sort()方法(升序数组元素)需要数组中的元素实现IComparable接口, Array.Reverse()方法降序排列数组元素(仅支持一维数组)
string[] name =
{
"one",
"two",
"three"
};
//Sort方法升序排列,把name按照26个英文首字母从小到大排序
Array.Sort(name);
//从索引为0的元素开始的3个元素进行升序
Array.Sort(name,0,3);
//整体降序
Array.Reverse(name);
//局部降序,对索引为0的元素开始的3个元素进行反转
Array.Reverse(name,0,3);
2. 对数组使用自定义类,就必须实现IComparable接口,这个接口中只定义了一个方法 CompareTo()
//当s1 > s2时,s1.CompareTo(s2) == 1
//当s1 = s2时,s1.CompareTo(s2) == 0
//当s1 < s2时,s1.CompareTo(s2) == - 1
string s1 = "apple";
string s2 = "blue";
数组可以作为参数传递给方法,也可以从方法返回
数组可以声明为基类,其派生类型的元素可以赋予数组元素,数组协变只能用于引用类型,不能用于值类型
public class PhoneCustomer
{
//FirstName属性包含get和set访问器,来检索和设置支持字段的值
public string FirstName { get; set; }
public int Age { get; set; }
}
public class SaveAccount : PhoneCustomer
{
private decimal _balance;
public void PayIn(decimal amount) => _balance += amount;
public bool Withdraw(decimal amount)
{
if(_balance > amount)
{
return true;
}
return false;
}
public decimal Banlance => _balance;
}
PhoneCustomer[] arr = new PhoneCustomer[3];
//给arr数组赋值
arr[0] = new PhoneCustomer() { FirstName = "a", Age =1};
//可以new派生类SaveAccount,把派生类SaveAccount继承的基类PhoneCustomer中的属性值赋值
arr[1] = new SaveAccount() { FirstName ="B", Age = 2};
ArraySegment
public static int GetSegmentSum(ArraySegment[] SegmentArray)
{
int sum = 0;
for (int i = 0; i < SegmentArray.Length; i++)
{
for (int j = SegmentArray[i].Offset; j < SegmentArray[i].Offset + SegmentArray[i].Count; j++)
{
// 这里只是给出一个简单的处理,是为了表明可以很方便的处理一个数组的不同部分
if (i == 0)
{
sum += SegmentArray[i].Array[j];
}
else
{
sum -= SegmentArray[i].Array[j];
}
}
}
return sum;
}
int[] arr1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// 创建部分数组,从arr1数组的下标5开始,长度是4
ArraySegment arr2 = new ArraySegment(arr1, 5, 4);
// 创建复制复制数组arr3,长度是 4
int[] arr3 = new int[4];
//从arr1数组下标5开始复制,到arr3数组下标0开始,复制长度是4最终 arr3 = {6,7,8,9}
Array.Copy(arr1, 5, arr3, 0, 4);
// 创建多个部分数组
ArraySegment[] arrar2 =
{
new ArraySegment(arr1,0,4),
new ArraySegment(arr1,5,4)
};
// 调用方法测试
int sum = GetSegmentSum(arrar2); // 6+7+8+9 = 30
1. IEumerator接口
2. foreach语句
3. yield return返回集合的一个元素,并移动到下一个元素上,yield可停止迭代
元组(Tuple)合并了不同类型的对象
1. 元组元素可以通过 Item < elementnumber > 属性访问,例如 Item1、 Item2、 Item3等,最多可以访问 Item7属性。Item1属性返回第一个元素,Item2返回第二个元素,依此类推。最后一个元素(第8个元素)将使用 Rest 属性返回。第8个位置用于嵌套元组,您可以使用Rest属性访问该位置
2. Tuple 中的 Item 是只读的,不支持修改
//Tuple最多支持8个不同类型,如果需要更多,可以在最后一个扩展为Tuple,一般不建议超过8个用扩展
var tuple = Tuple.Create>("tian", "mo", "chou", 4, 5, 6, 2.3, Tuple.Create(20, 20));
Tuple person = new Tuple (1, "Steve", "Jobs");
//item1值是Jobs
var item1 = person.Item2;
var item8 = tuple.Rest;
var lastitem = tuple.Rest.Item1;
public static Tuple Divide(int dividend, int divisor)
{
return Tuple.Create(dividend, divisor);
}
static void DisplayTuple(Tuple person)
{
}
元组缺点:
Item
的属性访问Tuple元素,这是不太合理的。1. ValueTuple 是一个轻量级的值类型,并支持强命名
2. ValueTuple 的属性就可以在创建之后进行修改
3. 使用ValueTuple,要先安装 System.ValueTuple
4. ValueTuple的数据成员是字段不是属性
//1. 使用构造函数来创建 ValueTuple
ValueTuple valueTuple = new ValueTuple(1, "Joydip", "Kanjilal");
//2. 使用 Create 方法
var valueTuple2 = ValueTuple.Create(1, "Joydip", "Kanjilal");
//3. 给成员名赋值相应的value来创建一个ValueTuple
var author = (Id: 1, FirstName: "Joydip", LastName: "Kanjilal");
//4. 员名 + 对应值 放置在左边来实现对 ValueTuple 的创建和初始化
//给ValueTuple 的属性分配名字
(int Id, string FirstName, string LastName) author2 = (1, "Joydip", "Kanjilal");
//利用扩展方法实现 System.Tuple 和 System.ValueTuple 之间的互转
var valueTuple3 = ValueTuple.Create(1, "Joydip", "Kanjilal");
var tuple3 = valueTuple3.ToTuple();
//使用ValueTuple从方法中返回多个值
static (int, string, string) GetAuthor()
{
return (Id: 1, FirstName: "Joydip", LastName: "Kanjilal");
}
ValueTuple更详细: 详解C# Tuple VS ValueTuple(元组类 VS 值元组) - 永远薰薰 - 博客园 (cnblogs.com)