FindControl()为什么会抛出NullReferenceException异常?

3

我正在构建一个用户控件(使用C#的ASP.NET 3.5)。

这个控件在很大程度上基于另一个类似的控件(剪切和粘贴继承,不幸的是,没有简单的方法来抽象这个)。导致我困扰的行直接从另一个控件复制,显示的大部分也是如此。

相关的代码行如下:

Panel pnlForm = (Panel)e.Item.FindControl("pnlForm");
Literal ltrAvailableCount = (Literal)e.Item.FindControl("ltrAvailableCount");

DropDownList drpLanguage = (DropDownList)pnlForm.FindControl("drpLanguage");
DropDownList drpShipTo = (DropDownList)pnlForm.FindControl("drpShipTo");
HiddenField hdnAvailableProductId = (HiddenField)pnlForm.FindControl("hdnAvailableProductId");

DropDownList drpQuantity = (DropDownList)pnlForm.FindControl("drpQuantity");
HiddenField hdnSelectedStyle = (HiddenField)e.Item.FindControl("hdnSelectedStyle");
Label lblStyleName = (Label)e.Item.FindControl("lblSelectedStyle");

在上面的代码中,pnlForm被正确地找到了,ltrAvailableCount也是。如果我跳到drpQuantity,它和后面的行都可以正常工作。然而,当我运行drpLanguagedrpShipTohdnAvailableProductId的行时,FindControl会抛出一个NullReferenceException
我的意思不是它返回了'null',然后我尝试访问返回对象的属性,而是FindControl方法抛出了异常。根据MSDN库的说法,这是不可能的——FindControl没有列出任何潜在的错误,它只是说如果找不到控件,它就返回null。
是什么原因导致FindControl会出现NullReferenceException呢?
-----------------编辑---------------
我还应该提到,如果我在立即窗口中运行任何三个有问题的行,我会得到正确的结果。当我调试时检查pnlFormControlCollection时,我也可以看到控件。
-----------------第二次编辑-------------------
为了确认,我添加了另一行: DropDownList notThere = (DropDownList)pnlForm.FindControl("notHere"); 控件notHere在页面上不存在。上述行编译通过(当然),当我运行调试器时,它可以正常运行。类型转换也没有问题。变量notThere只是null。
这个问题的根本原因并不是FindControl无法找到控件并尝试将null转换为DropDownList或其他控件。

1
你确定 pnlForm 不是空的吗?在调试时仔细确认一下。 - jrummell
你确定你调用FindControl的对象不是空的吗? - Robert Levy
想象一下,如果FindControl()返回null,那么你对DropDownList的转换会起作用吗?我只是在打出我所思考的内容... - Bazzz
@jrummell和Robert Levy - 是的。正如我在问题中所述。 - Jeff
你应该一并发布repeater和它的模板。 - Caspar Kleijne
3个回答

9

并不是FindControl引起了问题。

你的代码在一行上执行了两个操作 - FindControl()和对控件数据类型的强制转换。正是强制转换导致了问题,因为FindControl调用的结果是一个空值,而你试图将空值转换为控件。

这个问题困扰了我很多次,所以我学会了这样编码:

object oDropDown1 = pnlForm.FindControl("DropDown1");

// then check if oDrowpDown 1 is null and cast if it's safe.

if(oDropDown1 != null)
{
   // here it's safe to cast.
}

我不相信这是真的 - 这个模式在我们的项目中被广泛使用,在每一个其他情况下,我们都看到它可以很好地完成转换(尽管dropdownlist/label/control为空)。此外,如果它是无效的转换,那么肯定会抛出InvalidCastException而不是NullReferenceException吧? - Jeff
我修改了代码,使其看起来像你的示例,并返回一个调试器视为DropDownList的对象。在进行空值检查后,强制转换失败了。 - Jeff
好的,至少你知道失败的是转换而不是FindControl()...现在你可以在正确的地方查找了。 - David
@Jeff - 谢谢。出于好奇,解决方案是什么? - David
我还没有弄清楚这个问题,但它只出现在一个测试账户上。我们已经花费了3-4个人小时来解决它,但我的团队认为,除非在测试中再次出现(使用其他账户),否则没有必要追踪它。 - Jeff
显示剩余2条评论

7

null转换为DropDownList会导致异常。

如果pnlForm.FindControl("drpQuantity")返回null,则无法进行强制转换;)

但是以下方法可以解决问题:

System.Web.UI.WebControls.DropDownList drpQuantity = 
   pnlForm.FindControl("drpQuantity") as System.Web.UI.WebControlsDropDownList;

if(drpQuantity!=null){
   //use drpQuantity here
}

as运算符用于在兼容类型之间进行转换,与强制转换类似,但是在转换失败时返回null而不是引发异常。

无论如何,仅打算用findcontrol来查找数据生成的控件,在所有其他情况下,请使用接口(-实现)与用户控件上的控件通信。

在我看来,findcontrol是一种被全球滥用的功能.... (为了使用它而杀死小猫)


这是在重复器的ItemCommand中使用的,你所说的数据生成是指这个吗?此外,这些行来自UserControl的代码后台,而不是从包含页面访问它。 - Jeff
DropDownList drpLanguage = pnlForm.FindControl("drpLanguage") as DropDownList; 这也失败了,出现了相同的异常。当我尝试运行David Stratton建议的修改后的代码时,我检查了此行返回的FindControl对象 - 它不是null...所以将DropDownList强制转换为DropDownList失败了。 - Jeff
更新:将System.Web.UI.WebControls作为其命名空间添加,可能DropDownList不是System.Web.UI.WebControls.DropDownLIst。 - Caspar Kleijne
看起来是有道理的,但我检查了一下,它是正确的。另外,请参见我上面的“第二次编辑”,我故意添加了一行代码,其中FindControl将返回null并将其强制转换为DropDownList,但它并没有失败。 - Jeff

0

如果找不到你要查找的控件,你会收到一个 NullReferenceException 异常。

尝试:

DropDownList drpLanguage = (DropDownList)(pnlForm.FindControl("drpLanguage"));
DropDownList drpShipTo = (DropDownList)(pnlForm.FindControl("drpShipTo"));
HiddenField hdnAvailableProductId = (HiddenField)(pnlForm.FindControl("hdnAvailableProductId"));

不需要。它返回null,而不是抛出NRE。 - Jeff
2
如果你尝试进行强制类型转换,那么你会得到NRE。 - Theofanis Pantelides

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接