代码之家  ›  专栏  ›  技术社区  ›  wonde

为什么不能将方法参数声明为var类型

  •  4
  • wonde  · 技术社区  · 15 年前

    我想知道为什么不可能像var类型那样的方法参数

    private void myMethod(var myValue) {
       // do something
    }
    
    9 回复  |  直到 15 年前
        1
  •  9
  •   Mark Byers    14 年前

    你只能用 var 方法体中的变量。另外,变量必须在声明中赋值,并且必须能够从右侧的表达式中明确地推断出类型。

    在所有其他地方,您必须指定一个类型,即使理论上可以推导出一个类型。

    原因是编译器的设计方式。一个简单的描述是,它首先解析除方法体之外的所有内容,然后对每个类、成员等的静态类型进行完整的分析。然后在分析方法体时使用此信息,特别是用于推导声明为 var . 如果 var 如果允许在任何地方使用,则需要对编译器的工作方式进行较大的更改。

    您可以阅读Eric Lippert关于此主题的文章,了解更多详细信息:

        2
  •  7
  •   Wim Coenen    15 年前

    因为编译器通过查看赋值的右边来确定实际的类型。例如,这里确定它是一个字符串:

    var s = "hello";
    

    在这里决定 Foo :

    var foo = new Foo();
    

    在方法参数中,没有“赋值的右侧”,因此不能使用var。

        3
  •  7
  •   Michael Stum    15 年前

    posting by Eric Lippert 关于字段上不允许使用var的原因,该字段还包含为什么它在方法签名中不起作用的解释:

    让我简单介绍一下C编译器的工作原理。首先,我们运行每个源文件并执行“仅顶层”分析。也就是说,我们在嵌套的所有级别上标识每个名称空间、类、结构、枚举、接口和委托类型声明。我们分析所有字段声明, 方法声明 等等。事实上,除了方法体之外,我们解析所有的东西;那些,我们跳过,稍后再返回。
    […]
    如果我们有“var”字段,那么在分析表达式之前无法确定字段的类型,而这发生在我们已经需要知道字段类型之后。

        4
  •  7
  •   Paul Hollingsworth    15 年前

    请看朱丽叶的回答,以便更好地回答这个问题。

    因为在C中添加完整的类型推断太难了。 其他语言(如haskell和ml)可以自动推断最一般的类型,而无需声明它。

    另一个答案指出编译器“不可能”推断VaR的类型,但实际上原则上是可能的。例如:

    abstract void anotherMethod(double z, double w);
    
    void myMethod<T>(T arg)
    {
        anotherMethod(arg, 2.0); // Now a compiler could in principle infer that arg must be of type double (but the actual C# compiler can't)
    }
    

    让“var”方法参数原则上与通用方法相同:

    void myMethod<T>(T arg)
    {
        ....
    }
    

    不幸的是,您不能对两者都使用相同的语法,但这可能是因为C的类型推断是在以后才添加的。

    一般来说,语言语法和语义的细微变化可以将“确定性”类型推理算法转变为不可确定的类型推理算法。

        5
  •  6
  •   Juliet    15 年前

    ml、haskell、scala、f_、sml和其他语言可以很容易地从自己语言中的等价表达式中找出类型,这主要是因为它们从一开始就在设计时就考虑了类型推断。C不是,它的类型推断是作为访问匿名类型问题的一个临时解决方案附加上去的。

    我推测,真正的Hindley-Milner类型推断从来没有为C实现过,因为在一种如此依赖类和继承的语言中推导类型很复杂。假设我有以下课程:

    class Base { public void Print() { ... } }
    
    class Derived1 : Base { }
    
    class Derived2 : Base { }
    

    现在我有了这个方法:

    var create() { return new Derived1(); }
    

    这里的退货类型是什么?它是 Derived1 或者应该是 Base ?就这一点而言,应该是吗 object ?

    好吧,现在假设我有这个方法:

    void doStuff(var someBase) { someBase.Print(); }
    
    void Main()
    {
        doStuff(new Derived1());
        doStuff(new Derived2()); // <-- type error or not?
    }
    

    第一次呼叫, doStuff(new Derived1()) ,大概是力 doStuff 向类型 doStuff(Derived1 someBase) . 现在假设我们推断一个具体类型而不是一般类型 T .

    第二个电话呢? dostuff(新派生1()) ?是类型错误,还是概括为 doStuff<T>(T somebase) where T : Base 相反?如果我们在一个单独的、未引用的程序集中进行相同的调用——类型推断算法将不知道是使用窄类型还是更通用的类型。因此,我们将根据方法调用是来自程序集内部还是来自外部程序集,得到两个不同的类型签名。

    不能基于函数的用法来概括更广泛的类型。你基本上需要确定一个单一的混凝土类型,一旦你知道哪一种混凝土类型正在通过。所以在上面的示例代码中,除非显式转换为基类型, 道格斯 被约束为接受 导出1 第二个调用是类型错误。

    现在,这里的诀窍是确定一种类型。这里发生了什么:

    class Whatever
    {
        void Foo() { DoStuff(new Derived1()); } 
        void Bar() { DoStuff(new Derived2()); }
        void DoStuff(var x) { ... }
    }
    

    什么类型的 DoStuff ?就这一点而言,我们根据上面的内容知道 Foo Bar 方法包含一个类型错误,但从查找哪一个错误可以看出?

    在不更改c的语义的情况下解析类型是不可能的。在C中,方法声明的顺序对编译没有影响(或者至少不应该影响;)。您可以说方法首先声明(在本例中,是 方法)确定类型,因此 酒吧 有一个错误。

    这是可行的,但它也改变了c的语义:方法顺序的改变将改变方法的编译类型。

    但我们更进一步:

    // Whatever.cs
    class Whatever
    {
        public void DoStuff(var x);
    }
    
    // Foo.cs
    class Foo
    {
        public Foo() { new Whatever().DoStuff(new Derived1()); }
    }
    
    // Bar.cs
    class Bar
    {
        public Bar() { new Whatever().DoStuff(new Derived2()); }
    }
    

    现在正从不同的文件调用这些方法。什么类型的?在没有对编译顺序施加某些规则的情况下,无法决定:如果foo.cs在bar.cs之前编译,则类型由foo.cs确定。

    而我们 可以 将这些规则强加于C上,以使类型推断工作,这将极大地改变语言的语义。

    相比之下,ml、haskell、f_和sml非常支持类型推断。 因为 它们有这些限制:在声明方法之前不能调用方法,对推断函数的第一个方法调用确定类型,编译顺序对类型推断有影响等等。

        6
  •  1
  •   marc_s    15 年前

    “var”关键字在c和vb.net中用于 类型推断 -你基本上告诉C编译器:“你知道类型是什么”。

    “var”仍然是强类型的——你太懒惰了,无法写出类型并让编译器来计算它——基于赋值右侧的数据类型。

    在这里,在一个方法参数中,编译器无法弄清楚您真正的意思。怎样?你真正的意思是什么类型的?编译器无法从方法定义中推断类型,因此它不是有效的语句。

        7
  •  1
  •   Andrew Bezzub    15 年前

    因为C是类型安全的 坚强的 类型语言。在程序编译器的任何地方,总是知道您使用的参数类型。 var 关键字只是被引入了匿名类型的变量。

        8
  •  0
  •   vittore    15 年前

    检查 dynamic 在C 4中

        9
  •  0
  •   mrjoltcola    15 年前

    类型推理是类型推理,可以是局部表达式,也可以是全局/过程间的。所以这不是“没有右手边”,因为在编译器理论中,过程调用是“右手边”的一种形式。

    如果编译器进行了全局类型推断,C可以这样做,但它没有。

    如果需要接受任何内容的参数,可以使用“object”,但是需要自己处理运行时转换和潜在的异常。

    C中的“var”不是运行时类型绑定,它是一个编译时功能,最终得到一个非常特定的类型,但C类型推断的范围有限。