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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM