一起谈.NET技术,话说索引器、foreach和集合初始化器

  索引器

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

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


public class Capital
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }

/// <summary>
/// 定位
/// </summary>
public Location GlobalLoc { get; set; }
}

  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中的集合类,如果存储在内存中,则或多或少避免不了使用集合了。


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 类定义如下:


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

时间: 2024-10-21 19:09:49

一起谈.NET技术,话说索引器、foreach和集合初始化器的相关文章

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

索引器 在使用集合类时我们经常用到索引器,例如List集合,我们使用int数字作为索引器参数,而字典集合我们可以使用任何对象作为索引器参数. 在我们自己定义的类中使用索引器的情况也许不多,现在举例详细说明一下.假设我们定义首都类( Capital ): public class Capital { /// <summary> /// 名称 /// </summary> public string Name { get; set; } /// <summary> ///

C# 3.0语言新特性(语言规范):4 对象和集合初始化器

对象|规范|集合 原文:<C# Version 3.0 Specification>,Microsoft翻译:lover_P 一个对象创建表达式可以包含一个对象或集合初始化器,用于初始化新创建的对象的成员或新创建的集合的元素. object-creation-expression:new  type  (  argument-listopt  )  object-or-collection-initializeroptnew  type  object-or-collection-initia

C#3.0对象初始化器和集合初始化器

C# 3.0 中对象初始化器(Object Initializers) 和 集合初始化器(Collection Initializers) ,就是简化我们的代码,让本来几行才能写完的代码一行写完.这样在LINQ的使用中,我们才不会把一个LINQ表达式写的巨复杂无比. 由于我看到几篇讲 对象初始化器(Object Initializers)和集合初始化器(Collection Initializers) 的文章,都是一个简单的例子,一些稍稍特殊一点的场景的初始化赋值并没有涉及,所以我特整理这篇博客

C#3.0语言新特性之对象和集合初始化器

在C#3.0中,一个对象创建表达式可以包含一个对象或集合初始化器,用于初 始化新创建的对象的成员或新创建的集合的元素. 对象创建表达式: new type (argument-list(可选)) 对象或集合初试化器(可选) new type 对象或集合初试化器 一个对象创建表达式可以省略构造器参数列表,并将其连同圆括号一起替换 为一个对象或集合初始化器.省略构造器参数列表并将其连同圆括号一起替换为 一个对象或集合初始化器等价于指定一个空的参数列表. 在执行一个带有对象或集合初始化器的对象创建表达

一起谈.NET技术,.NET十年(上)

相关文章:.NET十年(下) 引言 语言是程序开发者行走江湖的手上利器,各大门派的高手在论坛.博客为了自家门派争吵不已早是技术世界中的亮丽风景,虽多少为刚刚踏入江湖的新手提供了思考的素材,但也同时迷惑了初出茅庐的前行方向. 本文不欲计较门派的高下,旨在明辨技术的真谛,这就是.NET平台下的开发利器:C#语言,并从其变迁的进程中对于.NET技术发展把玩一番. 本篇,我们走在历史,对.NET的过去尤其是c#语言做以回顾,具体而言就是.NET 1.0.2.0.3.0的一路走来. .NET之,历史脚步

Linq之隐式类型、自动属性、初始化器、匿名类

目录 写在前面 系列文章 隐式类型 自动属性 初始化器 匿名类 总结 写在前面 上篇文章是本系列的小插曲,也是在项目中遇到,觉得有必要总结一下,就顺手写在了博客中,也希望能帮到一些朋友.本文将继续介绍linq系列的基础知识,隐式类型,自动属性,初始化器,匿名类的相关概念,这些内容也许与linq相关也许不相关,但还是放一起总结吧,也算是复习了.部分内容通过反编译的方式一探究竟. 系列文章 Linq之Lambda表达式初步认识 Linq之Lambda进阶 隐式类型 先看看Msdn上对隐式类型的简单定

对象初始化器(Object Initializers)

对象初始化器(object initializers) 定义如下类   public class myclass  {    public int x;    public int y;    public myclass()    {    }    public myclass(int a, int b)    {      x = a;      y = b;    }  }对象初始化器object initializers 创建对象的时候可以使用如下语法: myclass mc = ne

一起谈.NET技术,使用 Silverlight Media Framework 构建自定义播放器

      流媒体已在网络上无处不在.似乎所有人(从新闻站点到社交网络再到隔壁的邻居)都在享受在线视频体验.由于用户群的不断攀升,大多数站点需要以一种值得信赖.用户友好的方式为其客户提供高品质视频,通常为高品质带宽感知 视频.       在线媒体传输体验的核心要素是播放器本身.播放器是用户与之发生交互的介质,它可以驱使用户获得在线体验的每一个要素.播放器如此备受关注,毫无疑问,基于 Web 的现代媒体播放器在实现时势必要比几年前的老款播放器复杂得多.因此,开发人员需要一个稳健的框架来构建播放器

一起谈.NET技术,《Effective C#中文版:改善C#程序的50种方法》读书笔记

从去年找工作以来,都没什么时间写博客[找工作的体会:建议以后有自己开公司的IT人一定要找IT专业人员做HR,好多公司的HR并不能真正发掘人才,他们形成了太多的偏见,如在学校期间学不了什么东西.只看学校有多少奖励等.真正钻研技术的人才不会追求虚无的东西],其实这本书我都借了好久,一直没有系统的看,所以趁这两天好好看看,顺便总结了一些要点,给那些需要这方面知识而又没有太多时间的IT人一个快速的学习机会....如果要深入学习,请购买该书. 一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定