c#中的泛型与数组

定义数组

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharp
{
    class Demo2
    {
        static void Main(string[] args)
        {
            //定义数组的5种方式(主要记住第一种和第三种即可)
            int[] num1 = new int[10];//指定数组的长度,声明的时候直接创建数组
            
            int[] num2;//先声明在创建数组
            num2 = new int[10];//创建数组

            int[] num3 = { 9, 8, 7, 6, 5, 4, 3, 2, 0, 1};//直接赋值决定成员的个数

            int[] num4 = new int[] { 1, 2, 4, 5, 6, 7 };//初始值来决定数组长度,和第三种一样的

            int[] num5 = new int[3] { 1, 2, 3 };//长度和所给的初始值一样 ,类似于第三种一样的

            //c#给的最大值和最小值常量
            int min = int.MaxValue;//这里用最大值存放最小值 ,在下面循环才能更好覆盖变量而已
            int max = int.MinValue;//c#最小值
            int sum = 0;
            //遍历数组foreach等价于for (int i = 0; i< num3.Length; i++)
            foreach (int i in num3)
            {
                if (i > max) max = i;//没有大括号只能写一句代码 
                if (i < min) min = i;//最小值:当值小于最大值,就得到最小值了 
                sum += i;
            }
            Console.WriteLine("num3的最大值是:{0},最小值是:{1},和为:{2},平均值:{3}",max,min,sum,sum/num3.Length);


            Console.Write("数组冒泡排序:");
            for (int i = 0; i < num3.Length - 1; i++)
            {//数组长度num3.Length是从1开始,而数组下标从0开始索引值,所以长度需要-1才能得到当前下标值
                for (int j = 0; j < num3.Length - 1 - i; j++) 
                {
                    if (num3[j] > num3[j + 1])
                    {
                        //交换顺序,升序排列
                        int temp = num3[j];//数组[0]下标为第一个。
                        num3[j] = num3[j + 1];
                        num3[j + 1] = temp;
                    }
                }
            }
            //c#给的数组排序(一句代码简化了冒泡排序)
            Array.Sort(num3);//等价于上面的for循环升序排列
            Array.Reverse(num3);//倒序,反转,单独使用就是从最后一个往前排列,但是配合Sort方法升序后在反转就变倒序了
            for (int i = 0; i < num3.Length; i++)
            {
                Console.Write(num3[i] + (i == num3.Length - 1 ? "" : ","));//括号里面使用了三目运算符,去掉了最后一个值后面的逗号
            } 




            Console.ReadKey();
            Console.ReadLine();//利用接收输入来暂停程序,避免程序一闪而过 

        }
    }
}

 索引器:封装数组的(数组是怎样形成的)从0下标开始索引每个值,给属性设置和读取,只需要【get只读和set只写】两个访问器,实体类也一样:可读可写两个访问器都要有

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharp
{
    class Demo2
    {
        static void Main(string[] args)
        {
            //创建一个班级
            ClassInfo c = new ClassInfo("t001");
            //创建几个学生
            StudentInfo s1 = new StudentInfo("t011021", "张三", "", 18);
            StudentInfo s2 = new StudentInfo("t011022", "李四", "", 18);
            StudentInfo s3 = new StudentInfo("t011023", "王五", "", 18);
            //添加数据
            c[0] = s1;
            c[1] = s2;
            c[2] = s3;

            c[1].SayHi();

            c["t011023"].SayHi();

            Console.ReadLine();//利用接收输入来暂停程序,避免程序一闪而过 

        }
    }
    class ClassInfo 
    {
        public ClassInfo(string ClassName) 
        {
            this.ClassName = ClassName;
        }
        public string ClassName { get; set; }

        //表示每个班级可以有10个学生
        StudentInfo[] s = new StudentInfo[10];

        /// <summary>
        /// 创建索引器,
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public StudentInfo this[int index] //只有int型的索引器才是可读可写的,否则都是get只读的
        {
            get { return s[index]; }
            set { s[index] = value; }
        }
        /// <summary>
        /// 索引器是可以重载的
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public StudentInfo this[string stuno] //只有int型的索引器才是可读可写的,否则都是get只读的
        {
            get { 
                foreach(StudentInfo  stu in s) 
                {
                    if (stu.stoun == stuno) return stu;
                }    
                return null; 
            }
        }
    }

    /// <summary>
    /// 班级实体类
    /// </summary>
    class StudentInfo
    {
        public StudentInfo(string stoun, string Name, string sex, int age) 
        {
            this.stoun = stoun;
            this.Name = Name;
            this.sex = sex;
            this.age = age;
        }
        public StudentInfo() { }
        public string stoun { get; set; }
        public string Name { get; set; }
        public string sex { get; set; }
        public int age { get; set; }

        public void SayHi() { Console.WriteLine("班级:{0},姓名:{1},性别:{2},年龄:{3}",stoun,Name ,sex,age); }

    }

}

 泛型

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Collections;//ArrayList集合和Hashtable容器所在的命名空间,非泛型,存放的是obj类型,需要装箱和拆箱,不安全,了解一下

namespace CSharp
{
    /// <summary>
    /// 随便定义一个接口,interface定义接口关键字,泛型具体约束参数用 where T:约束一个接口
    /// </summary>
    public interface People { }
    /// <summary>
    /// 泛型的使用:泛型就是广泛的意思,任意类型,发现重复的代码太多 ,只有条件不一样时就使用
    /// </summary>
    class Fanxing : People //继承接口
    {
        //无参构造方法
        public Fanxing() { }
        /// <summary>
        /// 有参方法:类本身默认是有个无参构造方法的给泛型约束用 创建这个有参构造是测试用 去掉参数就不报错了
        /// </summary>
        /// <param name="str">默认没有参数加上后就有了参数</param>
        public Fanxing(string str)
        {
        }
        static void Main(string[] aras)
        {
            //首先看看数组的容量是固定的,您只能一次获取或设置一个元素的值,而ArrayList或List<T>的容量可根据需要自动扩充、修改、删除或插入数据。

            string[] str = new string[4]; //限制长度为4  长度过长,会造成内存浪费,不清楚数组的长度,就会变得很麻烦。超过长度会报错,类型单一,针对缺点c#提高ArrayList对象来克服这些缺点。 
            str[0] = "a";//赋值
            str[1] = "b";

            str[1] = "a1";//修改

            //ArrayList是命名空间System.Collections下的一部分,同时继承了IList接口,提供了数据存储和检索。大小是按照其中存储的数据来动态扩充与收缩的。所以,在声明ArrayList对象时并不需要指定它的长度。
            ArrayList alc = new ArrayList();
            alc.Add("dsaf"); //添加数据,赋值,不受数据类型限制。长度可任意改变。每次集合中包含个数超过了可包含个数时会自动向内存申请多一倍的空间。来保证集合长度一直够用。
            alc.Add(321);

            alc[1] = 123;//修改数据
            alc.Remove("");//删除第一个匹配到值的方法
            alc.RemoveAt(0);//移除数据索引下标的删除方法
            alc.Insert(0, "qwe");//插入数据
            //alc.Sort();//排序 默认升序
            //alc.Reverse();//反转:升序反转后变成倒叙的意思
            alc.Contains("");//模糊查询,返回bool
            alc.IndexOf("");//查找值索引到下标,返回下标值,未找到返回 -1

            Console.WriteLine(alc[0].ToString() + Convert.ToInt32(alc[1])); //为什么需要类型转换?
            //我们ArrayList插入不同类型的数据是允许的当作为object类型来处理,需要处理类型不匹配的错误,不安全,用到装箱与拆箱的概念操作,缺点带来很大的性能耗损。
            //装箱:就是将值类型的数据打包到引用类型的实例中
            string icc = "abc";
            object obj1 = (object)icc;  //ArrayList存放的值 ,object是所有类型的基类
            //拆箱:就是从引用数据中提取值类型
            object obj2 = "abc";
            string j = obj2.ToString();  //拆箱了外面才能访问字符串

            //哈希表容器和ArrayList集合是一样的,存放的都是obj类型,需要装箱和拆箱,类型转换不安全。
            Hashtable ht = new Hashtable();//最原始的容器,这是了解这里不使用
            ht.Add(3, 22);//以键值对的方式存储,由于装拆箱一系列问题导致数据类型的不安全, 所以有了 字典 Dictonary<K,T>
            ht.Add("s", "");
            ht[1] = "值,如果有1这个键就覆盖值,没有就添加。 上面的add是纯添加数据。";
            int value = Convert.ToInt32(ht[3]);//非泛型键取值,需要拆箱:把obj类型转换为值类型
            foreach (var a in ht.Keys)//循环遍历所有键
            {
                Console.WriteLine("键:{0},值:{1}",a,ht[a]);
            }
            Console.WriteLine("判断是否包含某个键:" + ht.ContainsKey("s"));
            ht.Clear();//删除所有数据
            ht.Remove(3);//移除带3的键数据

            //键值对也叫字典Dictionary<键,值>:长度可动态改变,有数据类型约束,不需要装拆箱,操作更安全。
            Dictionary<int, string> dic = new Dictionary<int, string>();
            dic.Add(1, "张三");//对应的是键,值
            dic.Add(2, "李四");//键不能重复,值可以
            dic[3] = "我是新来的";
            //可以像哈希表一样根据键来遍历,这里用另外一种方法遍历
            foreach (KeyValuePair<int, string> kv in dic)
            {
                Console.WriteLine("{0}----->{1}", kv.Key, kv.Value);
            }

            //泛型List:因为ArrayList存在不安全类型与装箱拆箱的缺点,所以出现了泛型的概念。List类是ArrayList类的泛型等效类,它的大部分用法都与ArrayList相似,因为List类也继承了IList接口。最关键的区别在于,在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。

            List<int> ilit = new List<int>();  //这种限制了指定数据类型只能是int ,但是这种就不需要装箱和拆箱的步骤,其实这些方法都是由上面的慢慢演变过来的,也是ArrayList的一分部
            ilit.Add(1);
            ilit.Add(2);
            ilit.AddRange(new int[] { 3,4,5});
            ilit.AddRange(ilit);//自己添加自己
            //ilit.RemoveAt(0);//移除数据
            int[] ii = ilit.ToArray();//集合转为数组。
            ilit = ii.ToList();//数组转换为集合。
            foreach(var a in ilit)
            {
                Console.WriteLine(a);
            }
            
            //由于被数据类型限制所以学习到今天的知识点泛型List<T>,T表示任意类型,想看看下面两个数组遍历的完成时间顺序***********************************************************

            DateTime begin = DateTime.Now;
            List<int> ilist = new List<int>();//list默认是泛型类:泛型是类型安全的
            for (int i = 0; i < 100000; i++) { ilist.Add(i); }
            Console.WriteLine("list数组遍历完成用时:" + (DateTime.Now - begin).TotalMilliseconds + "毫秒");

            DateTime begin2 = DateTime.Now;
            //ArrayList不是泛型类,只是存放的object类型,可以存放任意类型,只是每次用需要装箱和拆箱(不安全):
            ArrayList al = new ArrayList();//任意类型变成obj类型存储在数组是装箱,则从数组拿出来是obj类型需要转换为对应类型输出显示叫拆箱。
            for (int i = 0; i < 100000; i++) { al.Add(i); }
            Console.WriteLine("ArrayList数组遍历完成用时:" + (DateTime.Now - begin2).TotalMilliseconds + "毫秒");

            //*****************************************************************************************************************************************************************



            //普通方法:每次调用都需要制定类型
            Console.WriteLine("得到int类型方法值:" + GetInt(1) + ",得到string类型方法的值" + GetStr("只能是字符串,int方法就只能是int类型的值,其他类型还需要继续定义。"));

            //泛型方法:GetAny<如果这里限制int类型>(这里参数就只能是int类型) 这叫泛型约束,不写就支持任意类型。
            Console.WriteLine(GetAny(6) + GetAny(".泛型方法可以传任意类型:") + GetAny<double>(1.101) + GetAny(true));

            //泛型具体约束
            //Console.WriteLine(GetT("显然引用类型会报错,他是有参构造,而约束的是无参数构造where T:new()"));
            Console.WriteLine(GetT(1) + ".泛型具体约束:" + GetT<double>(1.101) + GetT(true));
            Fanxing c = new Fanxing("约束new()就必须要有一个有参构造函数。");
            Console.WriteLine(GetT<Fanxing>(c));//如果没有一个参构造函数就报错
            Console.WriteLine(GetI<Fanxing>(c));//表示必须实现某个接口而且必须具有无参构造的函数,如果没有继承接口就报错

            //**************************************************************************************************************************************
            //泛型类
            MyList<string> ml = new MyList<string>();
            ml.Set("给自定义的泛型类做成list");
            Console.WriteLine(ml.Get());
            //数组类型的添加
            ml.Add("直接调用自定义的方法");
            ml.Add(",给数组转参数赋值,不过还达不到数组的效果!");
            Console.WriteLine(ml.GetArray(0) + ml.GetArray(1));//也是调用自定义的方法
            //使用索引设置和读取 
            ml[1] = "现在就有了数组的感觉,这种精神修改数组某个小标值";
            ml[0] = "读取直接用ml[索引数]";
            Console.WriteLine(ml[0] + ml[1] + "数组长度为:" + ml.length);//

            //MyList的遍历
            MyList<int> m = new MyList<int>();
            for (int i = 100; i < 200; i++) { m.Add(i); }
            //for(int i = 0; i< m.length; i++){ Console.WriteLine(m[i]); }
            //让MyList支持Foreach遍历,必须要实现至IEnumerable<T>接口的GetEnumerator()方法
            foreach (int item in m) { Console.WriteLine(item); }

            Console.ReadLine();//等待输入用来暂停
        }
        //************************************************************************************************************************
        //普通方法:不同参数,就要定义多个不同方法代码会越来越多
        public static int GetInt(int t)
        {
            return t;
        }
        public static string GetStr(string t)
        {
            return t;
        }
        //************************************************************************************************************************
        //上面2个普通方法归一成泛型方法,对我们感觉到方法一样自由类型不一样就使用泛型方法,解决代码的通用性

        /// <summary>
        /// 泛型方法:提高代码通用性,当你写代码时,绝大部分代码意义只有类型不一样就考虑费用泛型
        /// 泛型约束<M> 具体约束语法,如:public static M GetAny<M>(M m) where M:struct 表示必须是值类型 
        /// 其中  M:表示任意类型,其中where是类型约束,可用可不用,一般不喜欢用,看情况 where 约束  struct 必须是值类型  class 必须是引用类型 
        /// </summary>
        /// <typeparam name="M">大写字母数据类型表示泛型类型,可以是其他大写字母,常见T,自由定义,好区分就行</typeparam>
        /// <param name="m">变量名</param>
        /// <returns></returns>
        public static M GetAny<M>(M m) //where M:class //表示必须是引用类型;M:new() 
        {
            return m;
        }
        /// <summary>
        /// 泛型约束的具体约束:where T:new()表示必须有一个无参构造函数,值类型可以共存,引用类型是有参数构造函数不行
        /// </summary>
        public static T GetT<T>(T t) where T : new()
        {
            return t;
        }
        /// <summary>
        /// 泛型约束可以多约束:where I: People, new()  表示必须实现某个接口而且必须具有无参构造的函数
        /// </summary>
        public static I GetI<I>(I i) where I : People, new()
        {
            return i;
        }



        //****************************************************************************************************************************************
        /// <summary>
        /// 泛型类,然后实现至接口IEnumerable<T> 接口作用是里面封装了让MyList支持Foreach遍历核心方法 常用的list里面一样继承了这个接口 
        /// 泛型类 :换一种思维理解泛型 List<T>  List就是一个类名, add就是他的添加数据方法 ,同样的 MyList<T> my = new MyList<T>();也是一样道理,写好方法就可以
        /// </summary>
        /// <typeparam name="T">任意类型</typeparam>
        public class MyList<T> : IEnumerable<T> //接口里面封装了让MyList支持Foreach遍历核心两个方法 鼠标点击IEnumerable<T>实现接口自动生成
        {
            private T t;
            //设置
            public void Set(T _t)
            {
                t = _t;
            }
            //读取
            public T Get()
            {
                return t;
            }
            //**********************************************
            private T[] tArray = new T[4];//定义泛型数组 ,长度4
            public int length = 0;//存放数组长度
            //设置值
            public void Add(T _t)
            {
                //动态给数组扩容:如果数组满了,需要扩容
                if (length == tArray.Length)
                {
                    //创建一个新的数组,每次扩大4
                    T[] newarray = new T[tArray.Length + 4];

                    //把以前的数组内容放进新数组里面
                    Array.Copy(tArray, newarray, tArray.Length);//Array.Copy(复制这个数组,粘贴到这个数组,被复制数组的长度int类型)

                    //新的数组替换以前的数组
                    tArray = newarray;
                }
                tArray[length] = _t;
                length++;
            }
            //读取数组值
            public T GetArray(int _poi)
            {
                return tArray[_poi];
            }

            /// <summary>
            /// 索引器:封装数组从0下标开始索引每个值,给属性设置和读取,只需要【get只读和set只写】两个访问器,实体类也一样:可读可写两个访问器都要有
            /// </summary>
            /// <param name="index">索引下标:只有int型的索引器测试可读可写的,否则都是只读的get访问器</param>
            /// <returns></returns>
            public T this[int index]
            {
                get //只读:用来取值
                {
                    if (index >= length)
                    {
                        throw new Exception("索引超出了最大范围"); //抛出异常
                    }
                    return tArray[index]; //在实体类中默认就是返回给当前字段,没有字段变化,所以不写返回值。set服务器的value也是一样默认当前字段,不需要写
                }
                set //只写:用来设置一个值
                {
                    if (index >= length)
                    {
                        throw new Exception("索引超出了最大范围"); //抛出异常
                    }
                    tArray[index] = value; //value是set访问器自带的关键字,作用:将外部值传递进来,并赋值给当前字段。
                }
            }

            //让MyList支持Foreach遍历的核心方法 鼠标点击IEnumerable<T>实现接口自动生成的
            public IEnumerator<T> GetEnumerator()
            {
                //throw new NotImplementedException();//自动生成的没用

                for (int i = 0; i < length; i++)
                {
                    yield return tArray[i]; //yield 表示 不是马上return 而是方法执行一次返回一次,这里必须要加
                }
            }
            //和GetEnumerator一起生成的
            IEnumerator IEnumerable.GetEnumerator()
            {
                throw new NotImplementedException();
            }
        }
    }
}
原文地址:https://www.cnblogs.com/longxinyv/p/14428115.html