話說索引器、foreach和集合初始化器

作者: 喆_喆  來源: 博客園  發布時間: 2010-09-08 14:53  閱讀: 1382 次  推薦: 0   原文鏈接   [收藏]  

  索引器

  在使用集合類時我們經常用到索引器,例如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

0
0
 
標簽:索引器
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()