考虑这个有效的代码:
RadComboBox rcb = new RadComboBox(); // Telerik.Web.UI.RadComboBox
rcb.Items.Add(new RadComboBoxItem("One", "1"));
rcb.Items.Add(new RadComboBoxItem("Two", "2"));
rcb.Items.Add(new RadComboBoxItem("Three", "3"));
// check all items
foreach (RadComboBoxItem i in rcb.Items)
{
i.Checked = true;
}
如果我用var替换foreach循环,它就不会编译:
RadComboBox rcb = new RadComboBox(); // Telerik.Web.UI.RadComboBox
rcb.Items.Add(new RadComboBoxItem("One", "1"));
rcb.Items.Add(new RadComboBoxItem("Two", "2"));
rcb.Items.Add(new RadComboBoxItem("Three", "3"));
// check all items
foreach (var i in rcb.Items)
{
i.Checked = true;
}
错误是:
'object'不包含'Checked'的定义,也没有扩展方法'Checked'接受'object'类型的第一个参数可能是 找到了(您是否缺少using指令或程序集引用?)
所以,我想知道,当你不能使用var时会有什么条件?
编辑:只是为了澄清,我不是简单地问“为什么这不起作用”?这是一个可以使问题更加明显的例子:
List<Object> stuff = new List<object>();
stuff.Add("one");
stuff.Add("two");
stuff.Add("three");
foreach (string s in stuff)
{
if (s.Length > 3)
// do something
}
现在,如果您将字符串中的内容更改为 var s in stuff ,那么显然不会编译。你不能随便用 var 替换任何类型并期望它编译。我认为首先提出问题的错误在于我假设因为rcb.Items是RadComboBoxItemCollection类型,所以可枚举类型是RadComboBoxItem,而事实并非如此。
不幸的是,我的例子通过引导一些答案来玷污这个问题,“为什么用var替换一个不起作用的类型?”路径。保罗实际上回答了我正在寻找的东西(其他人也部分地做了),这就是为什么我给它最好的答案。
答案 0 :(得分:4)
您可以在任何时候使用隐式输入时使用var
。也就是说,当类型可以从声明派生时。在这种情况下,Items
的实际类型似乎是object
的集合。因此,所有var
都知道该声明。
通过明确地将类型声明为RadComboBoxItem
,您实际上是在做与此类似的事情(但需要更多的编译时检查):
foreach (var i in rcb.Items)
{
(i as RadComboBoxItem).Checked = true;
}
object
中的Items
个实例可以隐式转换为RadComboBoxItem
,但var
并不知道这样做。你必须明确地这样做。 (就个人而言,我会认为这是RadComboBox
的奇怪设计,但它可能是早期设计决策的延续。在.NET中的集合并不总是像今天这样强类型。像{{曾经导致过类似的问题。)
答案 1 :(得分:2)
for循环中出现这种情况的原因是编译器无法推断可枚举对象的类型。这发生在早于泛型的旧代码中 - 使用泛型,编译器能够推断迭代器中的下一项将是T
类型。但是,如果没有泛型,我们将返回非泛型IEnumerator
类型,其迭代仅显示对象。
请记住,for
循环基本上是用于在枚举器上调用Next()
方法,然后检查上述枚举器上的Current
属性的语法糖。较早类型的旧类型在其枚举器中没有泛型,因此它们总是返回一个对象(here是调用的实际方法。)
较新的强类型枚举器会在您调用T
时返回Current
类型,因此可以推断出类型。
另一个例子是在尝试迭代DataTable的行时System.Data命名空间。
在这种情况下,您应该明确说明变量的类型。
我很欣赏这可能是漫长的啰嗦,但我试图提供为什么不能在这里推断出类型和一些背景的原因,因为当我第一次尝试枚举{{1 }}
TLDR:
当您执行DataRowCollection
时,您将迭代非通用类型的枚举器。因为它不是一般类型,枚举器返回for
的实例。你会得到对象实例,并且在转换变量之前只能使用Object可用的方法。
答案 2 :(得分:1)
当rcb.Items返回RadComboBoxItemCollection
时,i
将变为object
类型。
您需要将其转换为RadComboBoxItem
(i as RadComboBoxItem).Checked = true;
或者像
一样修改循环foreach (var i in rcb.Items.Cast<RadComboBoxItem>())
{
i.Checked = true;
}
希望它有所帮助。
答案 3 :(得分:1)
RadComboBox
Items
property将返回RadComboBoxItemCollection
,它同时实现IEnumerable<RadComboBoxItem>
和非通用IEnumerable
。
虽然我不熟悉Telerik的产品,但我怀疑以前的版本没有实现IEnumerable<RadComboBoxItem>
。
根据C#语言规范,应工作(C#&gt; = 3.0第8.8.4节),用更简单的英语:
IEnumerable
,元素类型为数组的元素类型dynamic
,则会将其转换为IEnumerable
,如果{dynamic
,则元素类型为var
使用1}},否则object
IEnumerable
和 quacked 枚举器 quacks 类似IEnumerator
,则元素type是 quacked 枚举器的Current
属性的类型
IEnumerable
一样意味着它具有:
GetEnumerator
方法IEnumerator
一样意味着它具有:
Current
属性MoveNext
返回类型的公共非静态明确bool
方法GetEnumerator
方法后未满足任何条件,则会产生错误IEnumerable<T>
,则元素类型为T
IEnumerable<T>
,则会产生错误IEnumerable
,则元素类型为object
鉴于目前的版本,你的案件应该落在强调的子弹中。
但是,如果您使用以前的版本并且我的猜测是正确的,那么您的情况是倒数第二次。
答案 4 :(得分:0)
在这种情况下,使用var就像使用Object类型一样,因为Items是Object的集合
如果你想使用表达式i.Checked = true,你必须首先将你的i转换为RadComboBoxItem。
foreach(RadComboBoxItem i in rcb.Items)
{
i.Checked = true;
}
如果您希望使用此参数,这将是最好的(除非rct.Items包含除RadComboBoxItem类型之外的其他内容,在这种情况下,您可能必须找到正确的接口。