枚举和集合

一 枚举

其实如果把小孔所说的四种人当作四种枚举,里面包含他们可以干的事儿。那么:

君主要做君主该做的事儿,不能做臣子该做的事儿,并且只能做特定的事儿。说白了就是枚举各类人能做的事儿,枚举允许使用的值。比如下面这个例子:

 

枚举和int类型的数据可以互相强制转换,但是也会遇到一些问题:

 练习:

创建一个winform程序,弹出窗体,随机显示一张牌

首先枚举允许出现的扑克牌的花色和值

enum Suits
    {
        Spades,        //纸牌中的黑桃
        Clubs,          //梅花
        Diamonds,       //方块牌
        Hearts           //红桃心
    }
    enum Values
    {
        Ace = 1,
        Two = 2,
        Three = 3,
        Four = 4,
        Five = 5,
        Sex = 6,
        Seven = 7,
        Eight = 8,
        nine = 9,
        Ten = 10,
        Jack = 11,
        Queen = 12,
        King
    }
View Code

 接着创建Card类:

 class Card
    {
        public Suits suit { get; set; }
        public Values value { get; set; }
        public Card(Suits suit, Values value)
        {
            this.suit = suit;
            this.value = value;
        }
        public string Name
        {
            get { return value.ToString() + "\r\nof\r\n" + suit.ToString(); }
        }
    }
View Code

接着在一个按钮下:

 private void button1_Click(object sender, EventArgs e)
        {
            Card card = new Card((Suits)random.Next(4), (Values)random.Next(14));
            MessageBox.Show(card.Name);
        }
View Code

结果:

 二 集合

 

为啥要有集合:

在真实世界里,我们处理的数据绝对不会是-一点点。实际上,数据会成包、成捆,甚至成堆地压过来。我们需要一些非常强大的工具来整理所有这些数据,这就引入了集合。利用集合,可以存储程序需要处理的所有数据,并进行排序和管理。这样一来,你可以专心考虑编写程序处理数据,而由集合负责为你记录数据。集合有很多种,数组,List泛型集合,字典Dictionary集合,队列和线程队列,栈Stack......

1:数组

数组可以存储多个相同类型的数据,但是限制颇多

  • 存储的类型必须相同,也就是只能有一种类型
  • 长度固定,一旦开始声明了长度,不可改变
  • 处理数据非常麻烦,在数组上完成的所有逻辑计算都要由自己编写

所以可以考虑List集合

2:List集合【可以很容易的存储任何事物的集合】

例子:为一个List集合添加数种不同的鞋子

  enum Style
    {
        Sneakers,     //旅游鞋
        Loafers,      //平底鞋
        Sandals,      //拖鞋
        Filpflops,    //橡胶鞋
        Wingtips,     //孔鞋
        Clogs,        //木鞋
    }
    class Shoe
    {
        public Style Style;
        public string Color;
    }

主代码:

 class Program
    {
        static void Main(string[] args)
        {
            //List<Shoe> shoeList = new List<Shoe>();
            //shoeList.Add(new Shoe() { Style = Style.Sandals, Color = "black" });
            //shoeList.Add(new Shoe() { Style = Style.Wingtips, Color = "brown" });
            //shoeList.Add(new Shoe() { Style = Style.Clogs, Color = "black" });
            //shoeList.Add(new Shoe() { Style = Style.Filpflops, Color = "write" });
            //shoeList.Add(new Shoe() { Style = Style.Loafers, Color = "red" });
            //shoeList.Add(new Shoe() { Style = Style.Sneakers, Color = "uio" });
            //可以根据需要向List增加很多Shoe,只需要调用它的Add()方法。List会确保为这些项获得足够的“槽"(空间)。如果空间用完了,它会自动调整大小。
            List<Shoe> shoeList = new List<Shoe>()
            {
                new Shoe() { Style = Style.Sandals, Color = "black" },
                new Shoe() { Style = Style.Wingtips, Color = "brown" },
                new Shoe() { Style = Style.Clogs, Color = "black" },
                new Shoe() { Style = Style.Filpflops, Color = "write" },
                new Shoe() { Style = Style.Loafers, Color = "red" },
                new Shoe() { Style = Style.Sneakers, Color = "uio" },
            };
            //集合初始化方法类似于对象初始化方法C#提供了一个很不错的简便方法,要创建列表并在其中增加大量数据项,利用这种方法可以减少为此键入的代码。
            //创建新的List对象时,可以使用一个集合初始化方法( collection initializer) 来提供初始的-一个数据项列表。一旦列表创建就会增加这些数据项。  
            //集合初始化方法使代码更加紧凑,可以把创建列表和增加一组初始项结合在一起
            int listnumber = shoeList.Count;
            //返回list集合中的总数
            foreach (Shoe shoe in shoeList)
            {
                shoe.Style = Style.Filpflops;
                shoe.Color = "red";
            }
            //foreach是一种针对List的特殊循环。它会对List中的每个对象执行一个语句。
            //这个循环创建一个名为shoe的标识符。循环处理各项时,先设置shoe等于列表中的第一项,然后设置为第二项,再是第三项,直到循环结束。
            shoeList.RemoveAt(4);
            //Remove()方法根据对象引用删除一个对象,RemoveAt()则根据索引号删除对象。
            Shoe aa = shoeList[1];
            Shoe bb = shoeList[2];
            shoeList.Clear();
            //clear清除集合中的所有对象
            shoeList.Add(aa);
            if (shoeList.Contains(bb))
                Console.WriteLine("wu");

        }
    }
View Code

 List泛型集合可以存储任何类型

public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>

问:为什么要使用enum而不是List呢?它们不是解决同样的问题吗?

答:enum与List稍有一点区别。最重要的是,enum是类型,而List是对象。

可以把enum认为是存储常量列表的一种简便方法,以便按名来访问各个元素。enum对于保证代码可读性很有意义,另外可以确保你总是使用正确的变量名来访问经常用到的值。.

List可以存储任何对象。由于这是一个对象列表,列表中的每个元素可以有它自己的方法和属性。

所以,不能在enum中存储引用变量。另外,enum不能动态改变大小。枚举不能实现接口,也不能有方法,必须强制转换为另一种类型才能在其他变量中存储enum的值。

把以上几点综合起来,你应该了解到这两种存储数据的方法存在一些明显的差异。不过它们都非常有用。

问:听上去List的功能相当强大。那为什么有时还想使用数组呢?

答:数组还会使程序占用更少的内存,需要的CPU时间也更少,但这只会带来很小的一点性能提升。如果要在一秒内把同一件事做几百万次,你可能更

愿意使用数组而不是列表。不过,如果你的程序运行很慢,仅仅靠从列表转换为数组不太可能解决这个问题。 

如果你知道要处理的项数是固定的,或者你想要一个固定的定长值序列,数组就很完美。幸运的是,使用ToArray()方法可以很容易地把一

个列表转换为数组.......另外可以使用List<T>的某个重载构造函数把一个数组转换为列表。

问:我不懂“泛型(generic) ”是什么意思。为什么要称它是一个泛型集合呢?为什么数组不是泛型?

答:泛型集合(generic collection)是一个集合对象(或一个内置对象,可以存储和管理大量其他对象),这种集合只能存储一种类型(或者可以有多种类型)

问:嗯,这解释了“集合”部分,那什么是“泛型”呢?

答:超市经常用大包装装东西,包装标牌上只指出了包装的商品的名称(‘薯片可乐肥皂等)。这些通用标牌的重点是包装里的东西,而不是它如何显示。

泛型数据类型(generic data types)也是一样。不论其中是什么类型,List泛型集合的工作都完全一样,不论列表中是什么。

“泛型” (generic) 是指,尽管List的一个特定实例只能存储一种特定类型,

但List类适用于任何类型。这正是<T>的含义。通过这种方法可以将List的一个特定实例绑定到某种类型。

不过,List类本身很通用,完全可以处理任何类型。正是因为这一点,泛型集合与我们之前看到的类型是不同的。

问:能不能有无类型的列表?

答:不能。每一个列表,实际上,每个泛型集合(稍后还会了解其他泛型集合)都必须有一个与它关联的类

C#确实有一些非泛型列表,名为ArrayList,可以存储任何类型的对象。如果想使用ArrayList,需要在代码中包含一行“using System.Collections;”。

不过你不需要这么做,因为List<object>就很好!

创建一个新的List对象时,总要提供一个类型,告诉C#它会存储什么类型的数据。列表可以存储值类型(int ,bool或decimal),也可以存储一个类。

关于列表的排序问题:

  enum KindDuck
    {
        Mallard,         //野鸭
        Muscovy,         //麝香鸭
        Decoy            //诱饵鸭子
    }
    class Duck:IComparable<Duck>
    {
        public int Size;
        public KindDuck Kind;
        public int CompareTo(Duck ducktocompare)
        {
            if (this.Size > ducktocompare.Size)
                return 1;
            else if (this.Size < ducktocompare.Size)
                return -1;
            else
                return 0;
        }             
    }
Duck类

将鸭子填入列表容易,但是如何排序呢?

是按照鸭子的种类还是大小呢?

List知道如何排序,每个列表都提供了一个Sort方法,它会重排列表中的所有项,使他们有序排列。List已经知道如何对大多数内置类型和类进行排序,要教会它学会如何对你的定制类排序也很容易。

【理论上讲,并不是List<T>自己知道如何排序。它要依靠一个IComparer<T>对象 】

1.IComparable<Duck>帮助列表对鸭子排序

Llist.sort()方法知道如何对实现了IComparable<T>接口的类或类型排序。这个接口只有一个成员,即CompareTo()方法。Sort()使用一个对象的CompareTo()方法与其他对象比较,并使用其返回值(-个int) 来确定哪一个 在前。

但是有时需要对没有实现IComparable接口的对象列表排序,对此,.NET中的另一个接口可以提供帮助。可以向Sort()传入一个实现了IComparer<T>的类的实例这个接口也只有一个方法。

List的Sort()方法使用这个比较对象的Compare()方来比较一对对象,从而得出它们在有序列表中的先后顺序。

要让List的内置Sort()方法对某个类排序,只需让这个类实现IComparable<T>接口,并增加一个CompareTo()方法。

 

2.IComparer<Duck>帮助列表对鸭子排序

List有一个特殊的.NET Framework内置接口,允许你构建一一个 单独的类来帮助List<T>对它的成员的排序。通过实现IComparer<T>接口,可以告诉List你希望如何对对象排序。

为此,要实现IComparer<T>接口中的Compare()方法。它取两个对象参数x和y,并返回一个int。如果x小于y,这个方法应当返回一个负值。如果_二者相等,就返回0。如x大于y,这个方法要返回一个正值。

 

取决于如何实现IComparer<T>,List会以不同的方式进行排序。

 

 

结果:

IComparer可以完成复杂的比较,专门创建一个单独的类对鸭子排序有一个好处,这样可以在这个类中建立更复杂的逻辑,而且可以增加一些成员来帮助确定列表要如何排序。

 

 

 

 

 

练习:创建五张牌,然后对他们排序。

  enum Suits
    {
        Spades,        //纸牌中的黑桃
        Clubs,          //梅花
        Diamonds,       //方块牌
        Hearts           //红桃心
    }
    enum Values
    {
        Ace = 1,
        Two = 2,
        Three = 3,
        Four = 4,
        Five = 5,
        Sex = 6,
        Seven = 7,
        Eight = 8,
        nine = 9,
        Ten = 10,
        Jack = 11,
        Queen = 12,
        King
    }

    class Card
    {
        public Suits suit { get; set; }
        public Values value { get; set; }
        public Card(Suits suit, Values value)
        {
            this.suit = suit;
            this.value = value;
        }
        public string Name
        {
            get { return value.ToString() + "\r\nof\r\n" + suit.ToString(); }
        }
    }
Card类
 class CardCompareByValue:IComparer<Card>
    {
        public int Compare(Card x,Card y)
        {
            if (x.value < y.value)     //如果X牌面更大,返回1。如果X牌面小,则返回-1.要记住,这两个return语句都会立即结束这个方法。
                return -1;
            if (x.value > y.value)
                return 1;
            if (x.suit < y.suit)       //只有当XY两张排面大小相同时才会执行这些语句,这表示只有前两个return语句不执行时才会到这里
                return -1;
            if (x.suit > y.suit)
                return 1;
                return 0;             //如果这四个return语句都没有机会执行,说明这两张牌完全相同,则返回0
        }
    }
比较类
 static void Main(string[] args)
        {
            Random random = new Random();
            Console.WriteLine("Five Cards");
            List<Card> cards = new List<Card>();
            for (int i = 0; i < 5; i++)
            {
                cards.Add(new Card((Suits)random.Next(4), (Values)random.Next(1, 14)));
                Console.WriteLine(cards[i].Name);
            }
            Console.WriteLine();
            Console.WriteLine("Sort Five Card");
            cards.Sort(new CardCompareByValue());
            foreach (Card card in cards) 
            {
                Console.WriteLine(card.Name);
            }
            Console.ReadKey();
        }
main

结果:

 我们通过foreach循环来使列表的数据打印出来,那么foreach到底做了什么呢?

List<int> aaa = new List<int>();
            aaa.GetEnumerator();

Enumerator对象提供了循环机制,可以让你按照顺序循环处理列表,

集合实现IEnumerable<T>时,就为你提供了一种方法,可以编写循环按顺序处理循环处理里面的内容。

关于ToString[覆盖ToString()方法,让对象自我描述]

每个.Net对象都有一个ToString方法,这个方法可以把对象转化为一个字符串,默认的,返回类名,方法是从Object继承的,因为它是一个虚方法,所以只需要覆盖就好了。

【类中覆盖】

使用IEnumerable可以向上强制转换整个列表

这里需要理解:协变与逆变

“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。

“逆变”则是指能够使用派生程度更小的类型。

直白的理解:

“协变”->”和谐的变”->”很自然的变化”->string->object :协变。

“逆变”->”逆常的变”->”不正常的变化”->object->string 逆变。

3:字典集合【dictionary】

练习:构建一个使用字典程序

所有纽约棒球迷都会喜欢下面这个简单的小程序。一个明星球员退役时,球队也会让这个球员的球衣退役。下面来建立一个程序,查看那些著名号码的球衣是谁穿的,还有这些号码什么时间退役。

 class JerseyNumber
    {
        public string Player { get; private set; }
        public int YearRetired { get; private set; }
        public JerseyNumber(string player,int yearretired)
        {
            Player = player;
            YearRetired = yearretired;
        }
    }
JerseyNumber
 public partial class Form1 : Form
    {
        Dictionary<int, JerseyNumber> retirednumbers = new Dictionary<int, JerseyNumber>()
        {
            { 3,new JerseyNumber("qwer",0)},
            { 4,new JerseyNumber("ttu",1)},
            { 5,new JerseyNumber("qwer",2)},
            { 6,new JerseyNumber("zxc",3)},
            { 8,new JerseyNumber("sdf",4)},
            { 9,new JerseyNumber("ghj",5)},
        };
        public Form1()
        {
            InitializeComponent();
            foreach (int key in retirednumbers.Keys)
            {
                comboBox1.Items.Add(key);
            }
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            JerseyNumber jerseynumber = retirednumbers[(int)comboBox1.SelectedItem];
            textBox1.Text = jerseynumber.Player;
            textBox2.Text = jerseynumber.YearRetired.ToString();
        }
    }
窗体代码

 

 4:队列和栈

List和Dictionary对象是两个内置的泛型集合,它们是.NET Framework的一部分。列表和字典非常灵活,可以按任何顺序访问其中的任何数据。不过,有时需要对程序处理数据的方式加以限制,因为真实世界中往往都会有一些限制,程序也要表示出这一点。对于这种情况,要使用队列(Queue)或栈(Stack)。它们是另外两种泛型集合,与列表类似,不过更擅长按某种特定的顺序处理数据。

注释:【泛型集合是.NET Framework的一个重要部分泛型集合确实有用,正是因为它们如此重要,所以IDE会在你增加到工程的每个类最上面自动增加以下语句:

using System.collections.Generic;
你参与的几乎所有大工程都会包含某种泛型集合,因为你的程序需要存储数据。处理真实世界中一组类似的事物时,几乎都可以很自然地归为一类,而这可以很好地对应为某种集合。】

队列类似于一个列表·,可以再列表尾增加对象,而使用列表头的对象。栈只允许访问最后放入的对象。

队列是FIF0———先进先出
队列很像列表,只不过不能按任意索引增加或删除项。向队列增加一个对象时,需要将它入队 (enqueue)。这会把这个对象增加到队尾。可以从队头将第一个对象出队(dequeue)。此时,会从队列删除这个对象,队列中余下的对象会上移一个位置。

 

栈是LIFO--后进先出
栈与队列非常相似,但有一个重大区别。各个项要压入(push)栈,要从栈中取出一项时,需要从栈中弹出(pop)。从栈中弹出一项时,会得到最近压入的那一项。这有些像一摞盘子、杂志之类的东西,可以向栈顶放东西,但是需要把顶上的东西拿开才能拿到它下面的东西。

posted @ 2022-04-13 11:18  C#工控菜鸟  阅读(189)  评论(0编辑  收藏  举报