什么是闭包?.NET 中是否有闭包?
如果在.NET中存在闭包,你能提供一个代码片段(最好用C#)来解释它吗?
我有一篇关于这个话题的文章 (包含许多示例)。
本质上,闭包是一块代码,它可以在稍后的时间执行,但仍然保持它最初创建时的环境 - 即使该方法已经执行完毕,它仍然可以使用该方法所创建的局部变量等。
C#中通过匿名方法和Lambda表达式来实现闭包的一般特性。
这里是使用匿名方法的示例:
using System;
class Test
{
static void Main()
{
Action action = CreateAction();
action();
action();
}
static Action CreateAction()
{
int counter = 0;
return delegate
{
// Yes, it could be done in one statement;
// but it is clearer like this.
counter++;
Console.WriteLine("counter={0}", counter);
};
}
}
输出:
counter=1
counter=2
在这里我们可以看到,CreateAction返回的操作仍然可以访问计数器变量,即使CreateAction本身已经完成,它仍然可以增加计数器变量的值。
如果您对C#如何实现闭包感兴趣,请阅读“我知道答案(是42)博客”
编译器在后台生成一个类,以封装匿名方法和变量j
[CompilerGenerated]
private sealed class <>c__DisplayClass2
{
public <>c__DisplayClass2();
public void <fillFunc>b__0()
{
Console.Write("{0} ", this.j);
}
public int j;
}
对于这个函数:static void fillFunc(int count) {
for (int i = 0; i < count; i++)
{
int j = i;
funcArr[i] = delegate()
{
Console.Write("{0} ", j);
};
}
}
将其转换为:
private static void fillFunc(int count)
{
for (int i = 0; i < count; i++)
{
Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1();
class1.j = i;
Program.funcArr[i] = new Func(class1.<fillFunc>b__0);
}
}
闭包是函数值,它们保留了其原始范围内的变量值。在C#中,可以使用匿名委托来使用它们。
以一个非常简单的示例为例,看看这段C#代码:
delegate int testDel();
static void Main(string[] args)
{
int foo = 4;
testDel myClosure = delegate()
{
return foo;
};
int bar = myClosure();
}
最后,bar将被设置为4,并且myClosure委托可以传递到程序中的其他位置使用。
闭包可用于许多有用的事情,如延迟执行或简化界面 - LINQ主要是使用闭包构建的。对于大多数开发人员最直接的帮助是将事件处理程序添加到动态创建的控件中 - 您可以使用闭包在实例化控件时添加行为,而不是将数据存储在其他地方。
Func<int, int> GetMultiplier(int a)
{
return delegate(int b) { return a * b; } ;
}
//...
var fn2 = GetMultiplier(2);
var fn3 = GetMultiplier(3);
Console.WriteLine(fn2(2)); //outputs 4
Console.WriteLine(fn2(3)); //outputs 6
Console.WriteLine(fn3(2)); //outputs 6
Console.WriteLine(fn3(3)); //outputs 9
闭包是指在其创建的函数外部传递的匿名函数。它会保留其使用的来自创建它的函数的任何变量。
var workingDirectory = new DirectoryInfo(Environment.CurrentDirectory);//when this variable
Func<int, string> read = id =>
{
var path = Path.Combine(workingDirectory.FullName, id + ".txt");//is used inside this function
return File.ReadAllText(path);
};//the entire process is called a closure.
这是一个我从JavaScript类似代码中创造出来的C#虚构示例:
public delegate T Iterator<T>() where T : class;
public Iterator<T> CreateIterator<T>(IList<T> x) where T : class
{
var i = 0;
return delegate { return (i < x.Count) ? x[i++] : null; };
}
var iterator = CreateIterator(new string[3] { "Foo", "Bar", "Baz"});
// So, although CreateIterator() has been called and returned, the variable
// "i" within CreateIterator() will live on because of a closure created
// within that method, so that every time the anonymous delegate returned
// from it is called (by calling iterator()) it's value will increment.
string currentString;
currentString = iterator(); // currentString is now "Foo"
currentString = iterator(); // currentString is now "Bar"
currentString = iterator(); // currentString is now "Baz"
currentString = iterator(); // currentString is now null
var i = 0;
var items = new List<string>
{
"Hello","World"
};
var filtered = items.Where(x =>
// this is a predicate, i.e. a Func<T, bool> written as a lambda expression
// which is still a method actually being created for you in compile time
{
i++;
return true;
});
static void Main(string[] args)
{
Outlive();
var list = whereItems.ToList();
Console.ReadLine();
}
static IEnumerable<string> whereItems;
static void Outlive()
{
var i = 0;
var items = new List<string>
{
"Hello","World"
};
whereItems = items.Where(x =>
{
i++;
Console.WriteLine(i);
return true;
});
}
class <>c_DisplayClass1
{
public int i;
public bool <GetFunc>b__0()
{
this.i++;
Console.WriteLine(i);
return true;
}
}
var i = 0;
var items = new List<string>
{
"Hello","World"
};
var filtered = items.Where(x =>
{
i++;
return true;
});
filtered.ToList(); // will enumerate filtered, i = 2
i = 10; // i will be overwriten with 10
filtered.ToList(); // will enumerate filtered again, i = 12
Console.WriteLine(i); // should print out 12
闭包基本上是一段代码块,您可以将其作为参数传递给函数。C# 支持使用匿名委托形式的闭包。
以下是一个简单的例子:
List.Find 方法可以接受并执行一小段代码(闭包),以查找列表项。
// Passing a block of code as a function argument
List<int> ints = new List<int> {1, 2, 3};
ints.Find(delegate(int value) { return value == 1; });
使用 C#3.0 语法,我们可以这样写:
ints.Find(value => value == 1);
突然之间,来自C# 7.0概述书籍的简单易懂的答案。
先决条件:lambda表达式可以引用定义它的方法(外部变量)的局部变量和参数。
static void Main()
{
int factor = 2;
//Here factor is the variable that takes part in lambda expression.
Func<int, int> multiplier = n => n * factor;
Console.WriteLine (multiplier (3)); // 6
}
实际部分:由lambda表达式引用的外部变量称为捕获变量。捕获变量的lambda表达式称为闭包。
最后需要注意的一点:捕获的变量在委托实际调用时进行求值,而不是在捕获变量时进行求值:
int factor = 2;
Func<int, int> multiplier = n => n * factor;
factor = 10;
Console.WriteLine (multiplier (3)); // 30
counter
可以被递增 - 编译器生成一个包含counter
字段的类,任何引用counter
的代码最终都将经过该类的实例。 - Jon Skeet