开发者

Why it isn't possible to declare a method parameter as var type

I wonder why it is not possible a method parameter as var type like

private void myMethod(var myValue) {
   // do 开发者_JS百科something
}


You can only use var for variables inside the method body. Also the variable must be assigned at declaration and it must be possible to deduce the type unambiguously from the expression on the right-hand side.

In all other places you must specify a type, even if a type could in theory be deduced.

The reason is due to the way that the compiler is designed. A simplified description is that it first parses everything except method bodies and then makes a full analysis of the static types of every class, member, etc. It then uses this information when parsing the method bodies, and in particular for deducing the type of local variables declared as var. If var were allowed anywhere then it would require a large change to the way the compiler works.

You can read Eric Lippert's article on this subject for more details:

  • Why no var on fields?


Because the compiler determines the actual type by looking at the right hand side of the assignment. For example, here it is determined to be a string:

var s = "hello";

Here it is determined to be Foo:

var foo = new Foo();

In method arguments, there is no "right hand side of the assignment", so you can't use var.


See the posting by Eric Lippert about why var is not allowed on fields, which also contains the explanation why it doesn't work in method signatures:

Let me give you a quick oversimplification of how the C# compiler works. First we run through every source file and do a "top level only" parse. That is, we identify every namespace, class, struct, enum, interface, and delegate type declaration at all levels of nesting. We parse all field declarations, method declarations, and so on. In fact, we parse everything except method bodies; those, we skip and come back to them later.
[...]
if we have "var" fields then the type of the field cannot be determined until the expression is analyzed, and that happens after we already need to know the type of the field.


Please see Juliet's answer for a better answer to this question.

Because it was too hard to add full type inference to C#. Other languages such as Haskell and ML can automatically infer the most general type without you having to declare it.

The other answers state that it's "impossible" for the compiler to infer the type of var but actually it is possible in principle. For example:

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)
}

Have "var" method parameters is in principle the same thing as generic methods:

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

It is unfortunate that you can't just use the same syntax for both but this is probably due to the fact that that C#'s type inference was added only later.

In general, subtle changes in the language syntax and semantics can turn a "deterministic" type inference algorithm into an undecidable one.


ML, Haskell, Scala, F#, SML, and other languages can easily figure out the type from equivalent expressions in their own language, mainly because they were designed with type-inference in mind from the very start. C# wasn't, its type-inference was tacked on as a post-hoc solution to the problem of accessing anonymous types.

I speculate that true Hindley-Milner type-inference was never implemented for C# because its complicated to deduce types in a language so dependent on classes and inheritance. Let's say I have the following classes:

class Base { public void Print() { ... } }

class Derived1 : Base { }

class Derived2 : Base { }

And now I have this method:

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

What's the return type here? Is it Derived1, or should it be Base? For that matter, should it be object?

Ok, now lets say I have this method:

void doStuff(var someBase) { someBase.Print(); }

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

The first call, doStuff(new Derived1()), presumably forces doStuff to the type doStuff(Derived1 someBase). Let's assume for now that we infer a concrete type instead of a generic type T.

What about the second call, doStuff(new Derived1())? Is it a type error, or do we generalize to doStuff<T>(T somebase) where T : Base instead? What if we made the same call in a separate, unreferenced assembly -- the type inference algorithm would have no idea whether to use the narrow type or the more genenarlized type. So we'd end up with two different type signatures based on whether method calls originate from inside the assembly or a foreign assembly.

You can't generalize wider types based on usage of the function. You basically need to settle on a single concrete type as soon as you know which concrete type is being pass in. So in the example code above, unless you explicitly cast up to the Base type, doStuff is constrained to accept types of Derived1 and the second call is a type error.

Now the trick here is settling on a type. What happens here:

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

What's the type of DoStuff? For that matter, we know based on the above that one of the Foo or Bar methods contain a type error, but can you tell from looking which has the error?

Its not possible to resolve the type without changing the semantics of C#. In C#, order of method declaration has no impact on compilation (or at least it shouldn't ;) ). You might say instead that the method declared first (in this case, the Foo method) determines the type, so Bar has an error.

This works, but it also changes the semantics of C#: changes in method order will change the compiled type of the method.

But let's say we went further:

// 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()); }
}

Now the methods is being invoked from different files. What's the type? Its not possible to decide without imposing some rules on compilation order: if Foo.cs gets compiled before Bar.cs, the type is determined by Foo.cs.

While we can impose those sorts of rules on C# to make type inference work, it would drastically change the semantics of the language.

By contrast, ML, Haskell, F#, and SML support type inference so well because they have these sorts of restrictions: you can't call methods before they're declared, the first method call to inferred functions determines the type, compilation order has an impact on type inference, etc.


The "var" keyword is used in C# and VB.NET for type inference - you basically tell the C# compiler: "you figure out what the type is".

"var" is still strongly typed - you're just too lazy yourself to write out the type and let the compiler figure it out - based on the data type of the right-hand side of the assignment.

Here, in a method parameter, the compiler has no way of figuring out what you really meant. How? What type did you really mean? There's no way for the compiler to infer the type from the method definition - therefore it's not a valid statement.


Because c# is type safe and strong type language. At any place of your program compiler always knows the type of argument you are using. var keyword was just introduced to have variables of anonymus types.


Check dynamic in C# 4


Type inference is type inference, either in local expressions or global / interprocedural. So it isn't about "not having a right hand side", because in compiler theory, a procedure call is a form of "right hand side".

C# could do this if the compiler did global type inference, but it does not.

You can use "object" if you want a parameter that accepts anything, but then you need to deal with the runtime conversion and potential exceptions yourself.

"var" in C# isn't a runtime type binding, it is a compile time feature that ends up with a very specific type, but C# type inference is limited in scope.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜