C#基础知识点总结(五)- 数组元组

集合和数组:同一类型的多个对象

元组(Tuple):不同类型的多个对象

1. 简单数组

数组:同一类型的多个对象

1.1 数组的声明和初始化

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.2 访问数组元素

1. 数组只支持有整型参数的索引器,索引器总是从0开头,表示第一个元素

2. 数组的索引是myArray.Length - 1(数组长度减一,因为数组从0开始)

            //myArray.Length:数组的长度
            for (int i = 0; i < myArray.Length; i++)
            {
                Console.WriteLine(myArray[i]);
            }

1.3 使用引用类型

如果数组中的元素是引用类型,就必须为每个数组元素分配内存。若使用了未分配内存的元素,就会抛出异常

            //声明一个包含两个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"},
            };

2. 多维数组

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];

3. 锯齿数组

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 };

4. Array类

4.1 创建数组

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];

4.2 复制数组

数组是引用类型,如果一个数组变量赋予另一个数组变量,就会得到两个引用同一数组的变量。复制数组,会使数组实现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()方法必须传递阶数相同且有足够元素的已有数组

4.3 排序

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";        

5. 数组作为参数

数组可以作为参数传递给方法,也可以从方法返回

5.1 数组协变

数组可以声明为基类,其派生类型的元素可以赋予数组元素,数组协变只能用于引用类型,不能用于值类型

    
    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};

5.2 ArraySegment<T>

ArraySegment<T>表示数组的一段。有时只需要用到数组中某一段的数据。数组段不复制原数组的数据,但原数组可以通过ArraySegment<int>访问,如果数组段中的元素改变了,这些变化会反映到原数组中

 public static int GetSegmentSum(ArraySegment<int>[] 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<int> arr2 = new ArraySegment<int>(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<int>[] arrar2 =
            {
    new ArraySegment<int>(arr1,0,4),
    new ArraySegment<int>(arr1,5,4)
};

            // 调用方法测试
            int sum = GetSegmentSum(arrar2); // 6+7+8+9 = 30

6. 枚举

1. IEumerator接口

2. foreach语句

3. yield return返回集合的一个元素,并移动到下一个元素上,yield可停止迭代

7. 元组

7.1 Tuple

元组(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<string, string, string, int, int, int, double, Tuple<int, int>>("tian", "mo", "chou", 4, 5, 6, 2.3, Tuple.Create<int, int>(20, 20));
            Tuple<int, string, string> person = new Tuple <int, string, string>(1, "Steve", "Jobs");
            //item1值是Jobs
            var item1 = person.Item2;
            var item8 = tuple.Rest;
            var lastitem = tuple.Rest.Item1;

        public static Tuple<int, int> Divide(int dividend, int divisor)
        {
            return Tuple.Create<int, int>(dividend, divisor);
        }
        static void DisplayTuple(Tuple<int, string, string> person)
        {
        }

元组缺点:

  1. Tuple是一个引用类型,而不是一个值类型。它在堆上分配,并可能导致CPU密集型操作。
  2. Tuple被限制为包括八个元素。如果需要存储更多元素,则需要使用嵌套元组。但是,这可能导致歧义。
  3. 可以使用名称模式 Item <elementNumber> 的属性访问Tuple元素,这是不太合理的。

7.2 ValueTuple

1. ValueTuple 是一个轻量级的值类型,并支持强命名

2. ValueTuple 的属性就可以在创建之后进行修改

3. 使用ValueTuple,要先安装 System.ValueTuple

4. ValueTuple的数据成员是字段不是属性

            //1. 使用构造函数来创建 ValueTuple
            ValueTuple<int, string, string> valueTuple = new ValueTuple<int, string, string>(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)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值