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

索引器

在使用集合类时我们经常用到索引器,例如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中的集合类,如果存储在内存中,则或多或少避免不了使用集合了。

 

 

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

 

 

ExpandedBlockStart.gif 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.01f125.45f },三个参数被传入Add方法中。

 

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

 

 

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

转载于:https://www.cnblogs.com/szp1118/archive/2010/09/01/1814978.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值