C#var总是有效吗?

时间:2014-07-02 11:35:19

标签: c# var

考虑这个有效的代码:

    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替换一个不起作用的类型?”路径。保罗实际上回答了我正在寻找的东西(其他人也部分地做了),这就是为什么我给它最好的答案。

5 个答案:

答案 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,元素类型为数组的元素类型
  • (C#&gt; = 4.0)如果表达式类型为dynamic,则会将其转换为IEnumerable,如果{dynamic,则元素类型为var使用1}},否则object
  • 如果表达式quacks类型IEnumerable quacked 枚举器 quacks 类似IEnumerator,则元素type是 quacked 枚举器的Current属性的类型
    • Quacking 就像IEnumerable一样意味着它具有:
      • 具有类,结构或接口返回类型的公共非静态明确GetEnumerator方法
    • Quacking 就像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类型之外的其他内容,在这种情况下,您可能必须找到正确的接口。