C#中的闭包不是不可变的有什么好的理由吗?我一直在脑子里翻来覆去,似乎无法弄清楚为什么C#闭包是可变的。如果您不确切知道发生了什么,这似乎是获得意想不到后果的好方法。也许更有知识的人可以阐明为什么C#的设计者会允许状态在闭包内改变?例子:varfoo="你好";操作栏=()=>Console.WriteLine(foo);酒吧();foo="再见";酒吧();这将为第一次调用打印“hello”,但外部状态在第二次调用时发生变化,打印“bye”。闭包的状态被更新以反映局部变量的变化。C#和JavaScript,以及O'Caml和Haskell,以及许多其他语言都有所谓的词法闭包。这意味着内部函数可以访问封闭函数中局部变量的名称,而不仅仅是值的副本。当然,在具有不可变符号的语言中,比如O'Caml或Haskell,闭包名称与闭包值相同,因此这两种闭包的区别就消失了;然而,这些语言就像C#和JavaScript一样有词法闭包。并非所有闭包的行为都相同。存在语义差异。请注意,第一个想法与C#的行为相匹配......您的闭包语义概念可能不是主流。至于为什么:我认为这里的关键是ECMA,一个标准机构。在这种情况下,微软只是遵循他们的语义。这实际上是一个很棒的功能。这允许您有一个闭包访问通常隐藏的东西,比如私有类变量,并让它以受控的方式操纵它以响应事件等。通过创建变量的本地副本并使用它,您可以很容易地模拟您想要的东西。您还必须记住,在C#中,确实没有不可变类型的概念。因为.Net框架中的整个对象都没有被复制(你必须显式地实现ICloneable等),所以即使在闭包中复制了“指针”foo,这段代码也会打印“bye”:classFoo{publicstringText;}varfoo=newFoo();foo.Text="你好";操作栏=()=>Console.WriteLine(foo.Text);酒吧();foo.Text="再见";酒吧();因此,当前行为是否更有可能发生意想不到的后果值得怀疑。创建闭包时,编译器会为您创建一个类型,其中包含每个捕获变量的成员。在您的示例中,编译器将生成如下内容:[CompilerGenerated]privatesealedclassc__DisplayClass1{publicstringfoo;publicvoidb__0(){Console.WriteLine(this.foo);您的委托提供此类型,以便稍后可以使用捕获的变量。不幸的是,foo的本地实例也更改为指向此处,因此对本地的任何更改都会影响委托,因为它们使用相同的对象。如您所见,foo的持久性由公共字段而不是属性处理,因此在当前实现中甚至没有可选的不变性。我想你想要的必须是这样的:varfoo="hello";操作栏=[readonlyfoo]()=>Console.WriteLine(foo);酒吧();foo="再见";酒吧();请原谅笨拙的语法,但想法是表明foo是只读捕获的,然后提示编译器输出这个生成的类型:[CompilerGenerated]privatesealedclassc__DisplayClass1{publicreadonlystringfoo;publicc__DisplayClass1(stringfoo){this.foo=foo;}publicvoidb__0(){Console.WriteLine(this.foo);这会以某种方式给你你想要的,但需要更新编译器。至于为什么C#中的闭包是可变的,你不得不问,“你想要简单的(Java)还是复杂的函数(C#)?”可变闭包允许您定义它们一次并重用它们。例如:使用系统;使用System.Collections.Generic;使用System.Linq;使用系统文本;namespaceClosureTest{classProgram{staticvoidMain(string[]args){stringuserFilter="C";IEnumerablequery=(frommintypeof(String).GetMethods()wherem.Name.StartsWith(userFilter)selectm.Name.ToString()).Distinct();while(userFilter.ToLower()!="q"){DiplayStringMethods(query,userFilter);userFilter=GetNewFilter();}}staticvoidDiplayStringMethods(IEnumerablemethodNames,stringuserFilter){Console.WriteLine("这里是所有以字母"{0}"开头的字符串方法:",userFilter);控制台.WriteLine();foreach(stringmethodNameinmethodNames)Console.WriteLine("*{0}",methodName);}staticstringGetNewFilter(){Console.WriteLine();Console.Write("输入一个新的起始字母(输入"Q"退出):");ConsoleKeyInfocki=Console.ReadKey();控制台.WriteLine();返回cki.Key.ToString();}}}如果您不想定义一次并重新使用,因为您担心意外结果,您可将上面的代码更改为简单地使用变量的副本,如下所示:stringuserFilter="C";字符串userFilter_copy=userFilter;IEnumerablequery=(frommintypeof(String).GetMethods()wherem.Name.StartsWith(userFilter_copy)selectm.Name.ToString()).Distinct();现在,无论userFilter等于什么,查询都将返回相同的结果。JonSkeet很好地介绍了Java和C#闭包之间的差异。以上是C#学习教程:IsthereareanygoodreasonwhyclosuresnotimmutableinC#?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
