• 一起谈.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 { getset; }

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

      现在我们定义类似于集合的 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.01f125.45f }, 
                                                    { 
    "Seoul"37.33f126.58f }, 
                                                    { 
    "NewDelhi"28.36f77.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.01f125.45f },三个参数被传入Add方法中。

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

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

  • 相关阅读:
    20155203 《深入理解计算机系统》第五章学习总结
    2017-2018-1 20155203 20155204 实验五 通讯协议设计
    20155203 《信息安全系统设计基础》第十一周学习总结
    2017-2018-1 20155203 20155204 实验四 外设驱动程序设计
    课上第六章测试(补)
    20155203 《信息安全系统设计基础》第九周学习总结
    2017-2018-1 20155203 实验三 实时系统
    mypwd的编译和测试
    第二周 第三周 课下实践补交
    课上测试 补交及重做 深刻的教训
  • 原文地址:https://www.cnblogs.com/waw/p/2162689.html
Copyright © 2020-2023  润新知