动态(C# 4)和 var 有啥区别?

Posted

技术标签:

【中文标题】动态(C# 4)和 var 有啥区别?【英文标题】:What's the difference between dynamic (C# 4) and var?动态(C# 4)和 var 有什么区别? 【发布时间】:2010-10-31 23:41:11 【问题描述】:

我已经阅读了大量有关 C# v4 附带的新关键字的文章,但我无法区分“动态”和“变量”。

This article 让我思考了一下,但我还是看不出有什么不同。

是不是只能将“var”用作局部变量,而同时用作局部变量和全局变量?

你可以显示一些没有动态关键字的代码,然后用动态关键字显示相同的代码吗?

【问题讨论】:

【参考方案1】:

var 是静态类型的 - 编译器和运行时知道类型 - 它们只是为您节省一些输入...以下是 100% 相同的:

var s = "abc";
Console.WriteLine(s.Length);

string s = "abc";
Console.WriteLine(s.Length);

所有发生的事情是 编译器 发现 s 必须是一个字符串(来自初始化程序)。在这两种情况下,它都知道(在 IL 中)s.Length 表示(实例)string.Length 属性。

dynamic 是一个非常不同的野兽;它与object 最相似,但具有动态调度:

dynamic s = "abc";
Console.WriteLine(s.Length);

在这里,s 被键入动态。它不知道string.Length,因为它在编译时不知道关于s任何事情。例如,以下代码也可以编译(但不能运行):

dynamic s = "abc";
Console.WriteLine(s.FlibbleBananaSnowball);

在运行时(仅),它会检查 FlibbleBananaSnowball 属性 - 找不到它,并在一阵火花中爆炸。

使用dynamic,属性/方法/运算符/等在运行时根据实际对象进行解析。与 COM(可以具有仅运行时属性)、DLR 或其他动态系统(例如 javascript)通信非常方便。

【讨论】:

一个有趣的问题是静态声明的类是否存在动态祖先。示例:class X public int Y get;set; dynamic(X) s = GetSpecialX();调用字符串 test = s.Y;会产生编译器错误,因为编译器知道 Y 但 string test2 = s.Z 会编译良好并在运行时进行检查。我可以想到这种半动态类的价值! @rstevens - IIRC,您可以通过接口添加动态行为(尽管没有直接的语言支持在 C# 中实现动态类型 - 仅使用它们),所以这并非不现实......哦,我们可以有的乐趣;-p 尽管需要注意的是,有时var 可以推断出由于子类型和隐式转换而可能不需要的类型。也就是说,var 可能在发生 implicit 强制转换时解析了与预期不同的静态类型(最明显的是更通用的类型,但不限于此)。一个简单的例子是object x = "" vs. var x = "" vs. var x = "" as object,但可能会发生其他更偷偷摸摸(和现实)的情况,并可能导致细微的错误。 为了进一步阐述 Marc 的好例子,在第一种情况下(使用静态类型),编译器确切地知道要调用哪个 many overloads of WriteLine。这种“绑定”发生在编译时。对于dynamic.Length 的类型也必须是dynamic,直到运行时才决定WriteLine 的哪个重载(如果有的话)最适合。绑定发生在运行时。 在 Visual Studio 中悬停 var 关键字时,会显示正在推断的实际类型。显示类型在编译时是已知的。【参考方案2】:

var 声明的变量是隐式但 静态 类型的。用 dynamic 声明的变量是动态类型的。此功能已添加到 CLR 以支持动态语言,如 Ruby 和 Python。

我应该补充一点,这意味着 dynamic 声明在运行时解析,var 声明在编译时解析。

【讨论】:

【参考方案3】:

我将解释 dynamicvar 之间的区别。

dynamic d1;
d1 = 1;
d1 = "http://mycodelogic.com";

这会奏效。编译器可以重新创建 dynamic 变量的类型。 首先它将类型创建为 integer,然后编译器会将类型重新创建为 string 但在 var

的情况下
var v1;  // Compiler will throw error because we have to initialized at the time of declaration  
var v2 = 1; // Compiler will create v1 as **integer**
v2 = "Suneel Gupta"; // Compiler will throw error because, compiler will not recreate the type of variable 


使用'var'关键字时,类型由编译器在编译时决定,而使用'dynamic'关键字时,类型由编译器决定运行时。
var’关键字,一个强隐式类型的局部变量,编译器能够从初始化表达式中确定其类型 - 在进行 LINQ 编程时非常有用。
编译器没有关于动态类型变量的任何信息。所以编译器不会显示任何智能。编译器有关于 var 类型的存储值的所有信息,所以编译器会显示智能。
动态类型可以作为函数参数传递,函数也可以返回对象类型但是var类型不能作为函数参数传递,函数不能返回对象类型。这种类型的变量可以在它定义的范围内工作。

【讨论】:

【参考方案4】:

var 意味着应用了静态类型检查(早期绑定)。 dynamic 意味着应用了动态类型检查(后期绑定)。就代码而言,考虑以下几点:

class Junk

    public void Hello()
    
        Console.WriteLine("Hello");
    


class Program

    static void Main(String[] args)
    
        var a = new Junk();
        dynamic b = new Junk();

        a.Hello();

        b.Hello();
    

如果你编译它并使用 ILSpy 检查结果,你会发现编译器添加了一些后期绑定代码,这些代码将处理从 b 对 Hello() 的调用,而因为早期绑定应用于 a,所以 a 能够直接调用 Hello()。

例如(ILSpy反汇编)

using System;
namespace ConsoleApplication1

    internal class Junk
    
        public void Hello()
        
            Console.WriteLine("Hello");
        
    


using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Runtime.CompilerServices;
namespace ConsoleApplication1

    internal class Program
    
        [CompilerGenerated]
        private static class <Main>o__SiteContainer0
        
            public static CallSite<Action<CallSite, object>> <>p__Site1;
        
        private static void Main(string[] args)
        
            Junk a = new Junk();      //NOTE: Compiler converted var to Junk
            object b = new Junk();    //NOTE: Compiler converted dynamic to object
            a.Hello();  //Already Junk so just call the method.

                          //NOTE: Runtime binding (late binding) implementation added by compiler.
            if (Program.<Main>o__SiteContainer0.<>p__Site1 == null)
            
                Program.<Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Hello", null, typeof(Program), new CSharpArgumentInfo[]
                
                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                ));
            
            Program.<Main>o__SiteContainer0.<>p__Site1.Target(Program.<Main>o__SiteContainer0.<>p__Site1, b);
        
    

您可以做的最好的事情就是为自己编写一个类似这样的控制台应用程序,并使用 ILSpy 自己进行测试。

【讨论】:

关于 IL 在编译后如何处理它们的基本示例。谢谢。【参考方案5】:

一个很大的区别 - 你可以有一个动态的返回类型。

dynamic Foo(int x)

    dynamic result;

    if (x < 5)
      result = x;
    else
      result = x.ToString();

    return result;

【讨论】:

【参考方案6】:

这是一个简单的例子,展示了 Dynamic (4.0) 和 Var 之间的区别

dynamic  di = 20;
dynamic ds = "sadlfk";
var vi = 10;
var vsTemp= "sdklf";

Console.WriteLine(di.GetType().ToString());          //Prints System.Int32
Console.WriteLine(ds.GetType().ToString());          //Prints System.String
Console.WriteLine(vi.GetType().ToString());          //Prints System.Int32
Console.WriteLine(vsTemp.GetType().ToString());      //Prints System.String

**ds = 12;**   //ds is treated as string until this stmt now assigning integer.

Console.WriteLine(ds.GetType().ToString());          **//Prints System.Int32**

**vs = 12**; //*Gives compile time error* - Here is the difference between Var and Dynamic. var is compile time bound variable.

湿婆神

【讨论】:

我的印象是,代码示例中出现** 字符只是为了表示重点,而不是作为实际工作代码的一部分。【参考方案7】:

var 只是普通类型声明的简写,让编译器猜测正确的类型。

dynamic 是一种新的(静态)类型,所有检查都在运行时完成,而不是由编译器完成。

【讨论】:

【参考方案8】:

用var声明的变量的类型由编译器决定,它是指定类型名称的快捷方式,仅此而已。

不管动态是在运行时确定的,编译器不知道实际类型,所有使用该变量的方法/字段/属性访问都将在运行时计算出来。

【讨论】:

【参考方案9】:

This is a nice youtube video谈var VS Dynamic 实际演示。

下面是更详细的快照说明。

Var 是早期绑定(静态检查),而 dynamic 是后期绑定(动态评估)。

Var 关键字查看您右手边的数据,然后在编译时决定左手边的数据类型。换句话说,var 关键字只是节省您输入很多东西。看看下图,当我们给出字符串数据并且 x 变量在我的工具提示中显示字符串数据类型时。

另一方面,动态关键字的用途完全不同。动态对象在运行时进行评估。例如,在下面的代码中,“长度”属性是否存在在运行时进行评估。我特意输入了一个小的“l”,所以这个程序编译得很好,但是当它实际执行时,当“长度”属性时抛出一个错误被称为(小“l”)。

【讨论】:

【参考方案10】:

这里有区别

var 是静态类型的(编译时),dynamic 是动态类型的(运行时)

声明为 var 的变量只能在本地使用,动态的 变量可以作为参数传递给函数(函数签名 可以将参数定义为动态但不是 var)。

动态属性的解析发生在运行时和 var 不是这种情况,这意味着在编译时任何变量 声明为动态的可以调用可能存在也可能不存在的方法,并且 所以编译器不会抛出错误。

无法使用 var 进行类型转换,但可以使用动态类型转换(您可以将对象转换为动态但不能转换为 var)。

Arun Vijayraghavan

【讨论】:

【参考方案11】:

动态变量和var变量都可以存储任何类型的值,但需要在声明时初始化'var'。

编译器没有关于“动态”类型变量的任何信息。 var 是编译器安全的,即编译器具有有关存储值的所有信息,因此它不会在运行时引起任何问题。

动态类型可以作为函数参数传递,函数也可以返回它。 var 类型不能作为函数参数传递,函数不能返回对象类型。这种类型的变量可以在它定义的范围内工作。

动态转换不需要但是需要知道存储类型相关的属性和方法,而var不需要转换,因为编译器有所有信息来执行操作。

动态:在使用反射或动态语言支持或使用 COM 对象进行编码时很有用,因为我们需要编写更少的代码。

var:从 linq 查询中获取结果时很有用。在 3.5 框架中引入了支持 linq 功能。

参考:Counsellingbyabhi

【讨论】:

【参考方案12】:
    Var 和动态定义类型。 var 在编译时,而 dynamic 在运行时。 在 var 声明和初始化中都是强制的,就像常量变量 while 在动态初始化时可以像只读变量一样在运行时进行。 在 var 类型中,初始化时决定的任何类型都不能更改,但是 动态可以采用任何类型,甚至用户定义数据类型。

【讨论】:

【参考方案13】:

不要混淆动态和变量。 使用 var 声明局部变量只是一种语法快捷方式,它让编译器从表达式中推断出特定的数据类型。 var 关键字只能用于在方法中声明局部变量,而 dynamic 关键字可以用于局部变量、字段和参数。您不能将表达式强制转换为 var,但可以将表达式强制转换为动态。您必须显式初始化使用 var 声明的变量,而不必初始化使用 dynamic 声明的变量。

【讨论】:

【参考方案14】:
    Var(隐式类型局部变量)关键字用于定义局部变量。在 Var 的情况下,底层数据类型在编译时根据初始赋值本身确定。一旦使用 Var 类型进行初始赋值,那么它就会变成强类型。​​如果你尝试存储任何与 Var 类型不兼容的值,则会导致编译时错误。

例子:

Var strNameList=new List<string>(); By using this statement we can store list of names in the string format. 
strNameList.add("Senthil");
strNameList.add("Vignesh");

strNameList.add(45); // This statement will cause the compile time error.

但在动态类型中,底层类型仅在运行时确定。动态数据类型在编译时不检查,也不是强类型。我们可以为动态类型分配任何初始值,然后可以重新分配到其生命周期内的任何新值。

例子:

dynamic test="Senthil";
Console.Writeline(test.GetType())  // System.String

test=1222;
Console.Writeline(test.GetType())  // System.Int32

test=new List<string>();
Console.Writeline(test.GetType())  //System.Collections.Generic.List'1[System.String]

它也不提供 IntelliSense 支持。当我们使用 linq 时它也没有提供更好的支持。因为它不支持 lambda 表达式、扩展方法和匿名方法。

【讨论】:

以上是关于动态(C# 4)和 var 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

终于知道C#的动态类型有啥用了

动态朴素贝叶斯分类器和朴素贝叶斯分类器有啥区别

记忆化和动态编程有啥区别?

静态类型语言和动态类型语言有啥区别?

OpenMP 中的“静态”和“动态”调度有啥区别?

引用传递和动态内存分配之间有啥区别[关闭]