为什么foreach(HttpCookie cookie in Request.Cookies)会出错

    第一次使用foreach(HttpCookie cookie in Request.Cookies)的时候, 我怎么也没想到它会出错,错误信息竟然是“指定的转换无效。”。Request.Cookies的类型是HttpCookieCollection,怎么会出错呢?HttpCookieCollection难道有与众不同的地方?
    既然不能转换,那cookie究竟是什么类型?我们用代码测试一下:
   
foreach(object cookie in Request.Cookies)
            
{
                Response.Write(cookie.GetType().ToString()
+"<br>");
            }

原来cookie全变成了System.String类型, 上面的cookie变量的值就是Request.Cookies中每个Cookie的Name。下面的两段代码,结果是一样的:
 
foreach(object cookie in Request.Cookies)
            
{
                Response.Write(cookie
+"<br>");
            }

foreach(string cookie in Request.Cookies.AllKeys)
            
{
                Response.Write(cookie
+"<br>");
            }


那我们如何枚举Request.Cookies?通过索引,我也不用多说了,大家都知道。  

for(int i=0;i<Request.Cookies.Count;i++)
            
{
                Response.Write(Request.Cookies[i].Name
+":"+Request.Cookies[i].Value+"<br>");
            }


而Syste.Net.CookieCollection不存在这个问题,那HttpCookieCollection与CookieCollection有什么区别呢?我们比较一下它们的基类与接口:
HttpCookieCollection 继承了NameObjectCollectionBase,NameObjectCollectionBase实现了ICollection, IEnumerable, ISerializable, IDeserializationCallback接口
CookieCollection实现了ICollection, IEnumerable接口

我想应该在NameObjectCollectionBase中可以找到答案。
首先我们复习一下foreach的工作原理,先看下面的代码:

namespace Test
{
class Class1
{
   
static void Main(string[] args)
   
{
      
ArrayList array = new ArrayList();
      array.Add(
"A");
      array.Add(
"B");
      array.Add(
"C");

      
foreach (string item in array)
      
{
         Console.WriteLine(item);
      }

   }

}


在编译的时候,C#编辑器会对每一个foreach 区域进行转换,转换成下面的代码:


IEnumerator enumerator = array.GetEnumerator();
try 
{
   
string item;
   
while (enumerator.MoveNext()) 
   
{
      item 
= (string) enumerator.Current;
      Console.WriteLine(item);
   }

}

finally 
{
   IDisposable d 
= enumerator as IDisposable;
   
if (d != null) d.Dispose();
}

对于foreach(HttpCookie cookie in Request.Cookies), 转换的结果应该是这样:

IEnumerator enumerator = HttpCookieCollection.GetEnumerator();
try 
{
   HttpCookie item;
   
while (enumerator.MoveNext()) 
   
{
      item 
= (HttpCookie) enumerator.Current;
    }

}

finally 
{
   IDisposable d 
= enumerator as IDisposable;
   
if (d != null) d.Dispose();
}


因为上面的enumerator.Current返回的是System.String类型,所以会出现“指定的转换无效”的错误。
用Reflector查看NameObjectCollectionBase的源代码,可以发现 HttpCookieCollection.GetEnumerator()调用的是NameObjectCollectionBase中的GetEnumerator(),而GetEnumerator()返回的是NameObjectCollectionBase内部的一个类NameObjectKeysEnumerator的实例,这个类实现了IEnumerator。 
上面的enumerator.Current实际上是调用的NameObjectKeysEnumerator的Current属性,而Current属性中又调用了NameObjectCollectionBase的BaseGetKey。头都昏了! 为了找到问题的真正原因,只能继续。BaseGetKey的代码是这样的:

protected string BaseGetKey(int index)
{
      NameObjectCollectionBase.NameObjectEntry entry1 
= (NameObjectCollectionBase.NameObjectEntry) this._entriesArray[index];
      
return entry1.Key;
}

 


答案终于找到,NameObjectCollectionBase返回的是NameObjectEntry(这也是NameObjectCollectionBase的一个内部类)的一个成员Key, 这个Key就是string类型的。
问题的原因虽然找到,但为什么要这样设计,暂时还搞不明白,只能等以后慢慢研究。当然,如果有高人解惑就最好了。

posted on 2004-12-21 17:11 dudu 阅读(3258) 评论(15)  编辑 收藏 网摘

评论

#1楼 2005-03-03 16:10 张海霖(电子产品世界程序员)[未注册用户]

我也遇到了,没你那么认真研究。
System.Net.CookieCollection ccol = pc.postc(System.Web.HttpRuntime.Cache["loginurl"].ToString(),postData);//提交数据并返回Cookie

foreach(Cookie c in ccol)
{
....;
}
  回复  引用    

#2楼 2005-05-15 01:27 walkdan[未注册用户]

为什么HttpCookieCollection的Enumerator只对Key进行遍历,我想这主要是出于性能的考虑。返回一个Key比返回一个Cookie对象效率高。

你期望在对Cookies做遍历操作(Enumerate)时返回Cookie对象, 但Enumerator如果返回Cookie是需要做一个 new Cookie()操作,这意味着会来一个对象拷贝。有些Cookie对象是比较大, 拷贝效率非常低。

遍历Cookies可以这样:
foreach(string key in Request.Cookies)
{
Cookie cookie = Request.Cookies[key]; //没有产生新Cookie对象, 效率比较高。

// your code
}
  回复  引用    

#3楼[楼主] 2005-05-15 07:44 dudu      

@walkdan
谢谢你的指点!你的分析很有道理。
  回复  引用  查看    

#4楼 2005-05-15 14:39 walkdan

又想了一下,发现HTTPCookieCollection可以通过索引访问,最好的遍历方法应该用for而不是foreach:

for(int i=0; i < Request.Cookies.Count; i++)
{
HttpCookie cookie = Request.Cookies[i];

//......
}
这样即直观而且效率更高
  回复  引用    

#5楼[楼主] 2005-05-15 16:06 dudu      

文章中的解决方法就是通过索引访问。   回复  引用  查看    

#6楼 2007-12-19 11:34 txdlf      

源代码说明一切   回复  引用  查看    

#7楼 2008-06-25 11:24 姜敏      

dudu在04年的时候和我们一样,原来也是编码的啊.   回复  引用  查看    

#8楼 2008-06-25 11:27 姜敏      

这是与页面控件原理是一样的.当你遍历一个页面中所有的TEXTBOX时,就会出现和上面情况类似的现象.
for (int j = 0; j < this.Controls.Count; j++)
{
foreach (object o in Page.Controls[j].Controls)
{

if (o is TextBox)
{
TextBox txt = (System.Web.UI.WebControls.TextBox)o;
txt.Text = "A";
}

}
}
  回复  引用  查看    

#9楼 2008-06-25 11:28 姜敏      

foreach (object o in Page.Controls[j].Controls)
如果写成

foreach (Control o in Page.Controls[j].Controls)
就是出错
  回复  引用  查看    

#10楼 2009-03-29 10:33 玄天尊的小屋      

或许真的是为了asp人员的转换。。。。cookie[name]
  回复  引用  查看    

#11楼 2009-05-19 17:25 YBH      

受教了,写的真精彩   回复  引用  查看    

#12楼 2009-06-15 14:25 授人以愚[未注册用户]

原来是这样,学习了   回复  引用    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 80118




相关文章:

相关链接:

导航

公告

人生的真正价值在于从何种程度与何种意义上摆脱自我!
明天继续更新评论功能
<2004年12月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

统计

与我联系

搜索

 

常用链接

留言簿

随笔分类

随笔档案

新闻分类

相册

HJ

朋友的博客

网站收藏

小组

友情链接

最新随笔

最新评论

阅读排行榜

评论排行榜

60天内阅读排行