文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>话说索引器、foreach和集合初始化器

话说索引器、foreach和集合初始化器

时间:2010-09-01  来源:喆_喆

索引器

在使用集合类时我们经常用到索引器,例如List集合,我们使用int数字作为索引器参数,而字典集合我们可以使用任何对象作为索引器参数。

在我们自己定义的类中使用索引器的情况也许不多,现在举例详细说明一下:

假设我们定义首都类( Capital )

 1  public class Capital
 2     {
 3         /// <summary>
 4         /// 名称
 5         /// </summary>
 6         public string Name { get; set; }
 7 
 8         /// <summary>
 9         /// 定位
10         /// </summary>
11         public Location GlobalLoc { get; set; }
12     }

Capital类很简单,仅包含 名称和位置 两个属性,位置类包含经度和纬度两个属性,定义如下:

 public class Location
    {
        /// <summary>
        /// 经度
        /// </summary>
        public float Longitude { get; set; }

        /// <summary>
        /// 纬度
        /// </summary>
        public float Latitude  { get; set; }
    }

现在我们定义类似于集合的 CapitalList类

public class CapitalList
    {
        public string this[Location loc]
        {
            get
            {
                return Util.GetLocation(loc, this.GetHashCode());
            }
            set
            {
                Util.AddCapital(value, loc.Longitude, loc.Latitude,this.GetHashCode());
            }
        }

        public string this[float longitude, float latitude]
        {
            get
            {
                return Util.GetLocation(new Location { Longitude = longitude, Latitude = latitude }, this.GetHashCode());
            }
            set
            {
                Util.AddCapital(value, longitude, latitude,this.GetHashCode());
            }
        }

        public Location this[string name]
        {
            get
            {
                return Util.GetLocation(name, this.GetHashCode());
            }
            set
            {
                Util.AddCapital(name, value.Longitude, value.Latitude, this.GetHashCode());
            }
        }
    }

CapitalList实现了三个索引器(索引器可以重载),只要赋值就可以将记录保存下来(实际是保存在xml文件中,稍后说明)

l["Paris"] = new Location { Latitude = 48.5f, Longitude = 2.2f };
l[new Location { Latitude = 51.3f, Longitude = 0.1f }] = "London";
l[27f, 30f] = "Cairo";

生成的xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<CapitalList>
  <Paris>
    <Longitude>2.2</Longitude>
    <Latitude>48.5</Latitude>
  </Paris>
  <London>
    <Longitude>0.1</Longitude>
    <Latitude>51.3</Latitude>
  </London>
  <Cairo>
    <Longitude>27</Longitude>
    <Latitude>30</Latitude>
  </Cairo>
</CapitalList>

这里使用xml文件作为存储方式主要是想脱离.net中的集合类,如果存储在内存中,则或多或少避免不了使用集合了。

 

 

Util类  public static class Util
    {
        public static void AddCapital(string name, float longitude, float latitude, int identifier)
        {
            var xmlName = identifier + ".xml";
            var xe = new System.Xml.Linq.XElement(name,
                new System.Xml.Linq.XElement("Longitude", longitude.ToString()),
                new System.Xml.Linq.XElement("Latitude", latitude.ToString()));

            System.Xml.Linq.XElement x;
            try
            {
                x = System.Xml.Linq.XElement.Load(xmlName);
                var c = x.Elements(name);
                if (c != null)
                {
                    foreach (var te in c) {
                        te.Remove();
                    }
                }
            }
            catch (System.IO.FileNotFoundException)
            {
                x = new System.Xml.Linq.XElement("CapitalList");
            }
            x.Add(xe);
            x.Save(identifier + ".xml");
        }

        public static Location GetLocation(string name, int identifier)
        {
            var xmlName = identifier + ".xml";

            var x = System.Xml.Linq.XElement.Load(xmlName);
            var c = x.Elements().Where(t => t.Name == name);
            if (c == null || c.Count() == 0)
            {
                return null;
            }
            return new Location { Latitude = float.Parse((string)(c.First().Element("Latitude"))), Longitude = float.Parse((string)(c.First().Element("Longitude"))) };
        }

        public static string GetLocation(Location loc, int identifier)
        {
            var xmlName = identifier + ".xml";

            var x = System.Xml.Linq.XElement.Load(xmlName);
            var c = x.Elements().Where(t => float.Parse((string)(t.Element("Latitude"))) == loc.Latitude && float.Parse((string)(t.Element("Longitude"))) == loc.Longitude);

            if (c == null || c.Count() == 0)
            {
                return null;
            }

            return c.First().Name.LocalName;
            
        }
    }

 索引器其实是属性的一个特殊表示,可以赋值当然也可以取值(可以通过是否包含get和set访问器进行限制)

上述赋值之后下面就可以进行取值了

 

Console.WriteLine(l[27f, 30f]);
Console.WriteLine(l[new Location { Latitude = 51.3f, Longitude = 0.1f }]);
Console.WriteLine("{0},{1}",l["Paris"].Latitude, l["Paris"].Longitude);

 

 输出如下:

Cairo
London
48.5,2.2

 

到这里为止我们的CapitalList似有有点集合的味道了,接下来我要让我们自己的类能够使用foreach的语法

 

实现IEnumerable<T> 接口,支持foreach的语法

这里我们让类实现IEnumerable<Capital>接口,这样在使用 foreach遍历的时候类型就是  Capital 类型,需要注意的是,该类型和索引器使用的类型毫无联系,在之前的索引器中我们并不使用 Capital 类型。

 IEnumerable<T> 接口 只有一个方法需要实现即 GetEnumerator()方法,返回的是 实现了IEnumerator<T>接口的类,这个就是标准的迭代器模式。

 实现IEnumerable<Capital>接口的CapitalList类修改类如下:

    public class CapitalList : IEnumerable<Capital>
    {
        //索引器部分和原先的一样

        public IEnumerator<Capital> GetEnumerator()
        {
            return new CapitalEnumerator(this.GetHashCode());
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

这里的 CapitalEnumerator 类定义如下:

 

 

CapitalEnumerator  public class CapitalEnumerator : IEnumerator<Capital>
    {
        private string fname;

        public CapitalEnumerator(int identifier)
        {
            fname = identifier + ".xml";
        }

        private System.Xml.Linq.XElement xe;

        public Capital Current
        {
            get
            {
                return new Capital
                {
                    Name = xe.Name.LocalName,
                    GlobalLoc = new Location
                    {
                        Latitude = float.Parse((string)(xe.Element("Latitude"))),
                        Longitude = float.Parse((string)(xe.Element("Longitude")))
                    }
                };
            }
        }

        public void Dispose()
        {
           
        }

        object System.Collections.IEnumerator.Current
        {
            get { throw new NotImplementedException(); }
        }

        public bool MoveNext()
        {
            if (xe == null)
            {
                var x = System.Xml.Linq.XElement.Load(fname);

                if (x.FirstNode == null)
                    return false;
                
                xe = (System.Xml.Linq.XElement)(x.FirstNode);
                return true;
            }

           var n = xe.NextNode;

           if (n == null)
               return false;
           else
           {
               xe = (System.Xml.Linq.XElement)n;
               return true;
           }
        }

        public void Reset()
        {
            xe = null;
        }
    }

 

foreach只能单向循环,这里就可以看得出来,只有 MoveNext 方法,该方法返回是否还有下一个元素,并且设置下一个元素作为Current的值,真正的foreach中的当前元素的值就是从 Current 中取得的。

 

下面测试一下:

 foreach (var e in l) {
                Console.WriteLine("{0}:{1},{2}", e.Name, e.GlobalLoc.Latitude, e.GlobalLoc.Longitude);
            }

 

输出:

Paris:48.5,2.2
London:51.3,0.1
Cairo:30,27

至此,我们的CapitalList类已经成功支持了foreach语法了,和普通的集合类又近了一步。

 

 

添加Add方法,让类支持集合初始化器

在.net的集合类中我们可以使用集合初始化器初始化集合,例如

 List<int> l = new List<int> { 6, 3, 5, 8 };

如果不使用集合初始化器,就得使用如下代码

List<int> l = new List<int>();
            l.Add(6);
            l.Add(3);
            l.Add(5);
            l.Add(8);

 

让类支持集合初始化器非常简单,只需要在类中添加Add方法即可。

在CapitalList类中添加Add方法

public void Add(string name, float longitude, float latitude)
        {
            this[name] = new Location { Latitude = latitude, Longitude = longitude };
        }

 

这样我们就实现了集合初始化器。

 

  CapitalList l2 = new CapitalList { { "Pyongyang", 39.01f, 125.45f }, 
                                                { "Seoul", 37.33f, 126.58f }, 
                                                { "NewDelhi", 28.36f, 77.12f } };
            foreach (var e in l2)
            {
                Console.WriteLine("{0}:{1},{2}", e.Name, e.GlobalLoc.Latitude, e.GlobalLoc.Longitude);
            }

 

输出:

Pyongyang:125.45,39.01
Seoul:126.58,37.33
NewDelhi:77.12,28.36

集合初始化器中的参数表示形式和Add方法一致,上述的Add方法需要三个参数,集合初始化器中通过大括号中包含三个参数来表示,{ "Pyongyang", 39.01f, 125.45f },三个参数被传入Add方法中。

 

集合初始化器比较奇怪的地方的是需要在类中硬编码Add方法,而不是通过接口实现,这个不知道微软是出于什么考虑

 

 

以下是全部代码 /Files/szp1118/HelloWorld.rar

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载