This article is currently in the process of being translated into Chinese (~88% done).
Local functions
之前的章节讲解了在C#中方法和属性是隶属于类的。在方法内部,还能定义本地变量,属于只能在本方法内部访问的变量。这是合理的,因为经常会需要保存临时的数据,但这些数据不应该被其它的类,甚至本类的其它方法访问。此前,还没法这样处理方法 - 如果在类里面定义了方法,则此方法最少能被同一个类里的其它方法访问,不过在C#版本7中 ,引入了local functions(本地方法)的概念。
本地方法定义在一个现有的方法内部,只能在此方法内部访问。这对功能进行了非常严实的封装,也能向看这份代码的人清楚地显示此功能只与其定义地的那个方法有关。本地方法看来起与常规方法是一样的,但是没有可见性修饰符,因为本地方法永远都只能在其定义地的那个方法内部访问。下面是一个例子:
public void MethodWithLocalFunction()
{
bool doesNameStartWithUppercaseChar(string name)
{
if(String.IsNullOrEmpty(name))
throw new Exception("name parameter must contain a value!");
return Char.IsUpper(name[0]);
}
List<string> names = new List<string>()
{
"john doe",
"Jane doe",
"dog Doe"
};
foreach(string name in names)
Console.WriteLine(name + " starts with uppercase char: " + doesNameStartWithUppercaseChar(name));
}这虽然是个很笨的例子,但仍然能展示出怎样在一个方法(MethodWithLocalFunction)内部定义一个本地方法(本例中为doesNameStartWithUppercaseChar),然后在定义地方法内部一次或多次调用此本地方法。
在上面例子中,在方法的一开始就定义了一个本地函数(方法)。不过这是不必需,本地方法完全可以定义在其所属方法的中间或最后。只有一点需要注意:本地方法是被允许访问其所在方法内部的变量的,但这些变量必需要在本地方法之前定义。因此,如果属于这种情况,就得做些变动,象这样:
public void MethodWithLocalFunction()
{
int nameMaxLength = 10;
List<string> names = new List<string>()
{
"john doe",
"Jane doe",
"dog Doe"
};
foreach(string name in names)
Console.WriteLine(name + " starts with uppercase char: " + doesNameStartWithUppercaseChar(name));
bool doesNameStartWithUppercaseChar(string name)
{
if(String.IsNullOrEmpty(name))
throw new Exception("name parameter must contain a value!");
if(name.Length > nameMaxLength)
throw new Exception("name is too long! Max length: " + nameMaxLength);
return Char.IsUpper(name[0]);
}
}
这里在所属方法里面定义了一个nameMaxLength变量,然后在在本地方法中问题这个变量。
静态本地方法
到了C#版本8,又增加了静态本地方法的概念。顾名思义,常规及静态本地方法的唯一区别就是静态本地方法不可以访问所属方法内部的变量 - 换言之,静态方法不再与所属方法共享作用域了。因此,如果需要禁止本地方法访问或改变其所属方法内的变量,将其定义为静态的即可,象这样:
public void MethodWithLocalStaticFunction()
{
int nameMaxLength = 10;
static bool doesNameStartWithUppercaseChar(string name)
{
// Local variables, e.g. nameMaxLength, are no longer accessible here....
if(String.IsNullOrEmpty(name))
throw new Exception("name parameter must contain a value!");
return Char.IsUpper(name[0]);
}
....
}总结
某些情况下,当需要封装及重用特定功能时,本地方法可能会小有帮助。不过,如果该功能还能被其它的方法或类所重用,可能就应用考虑把其作为一个常规方法加入到某个帮助类里面,或作为一个扩展方法了。