星空网 > 软件开发 > ASP.net

C# 7.0 新特性2: 本地方法

本文参考Roslyn项目中的Issue:#259.

  1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法

 

简而言之,【本地方法】就是在方法体内部定义一个方法

其实咋眼一看,这个新特新并没有什么新意,因为目前大量C#的项目中,都可以使用delegate或基于delegate变形的各种方案(lambda, Fun<*>, Action, Action<*> ...)。

但是请仔细推敲一下,方法体内部delegate,是否真的那么完美无缺……

 

目前的C#

我们看一下,通常方法内部设置子逻辑单元的做法。

1 public void Bar()2 {3   var arr= new int[] { 5,8 ,10, 20 };4   Func<int, string> handler = i => $"当前值是:{i}。"; 5   foreach (var i in arr)6   {7     Console.WriteLine(handler(i));8   }9 }

看起来有什么问题吗?当然,很多。

  1. 首先,handler是无法进行递归调用的,看下面的代码。

1 public int Bar(int n)2 {3   if (n < 0) throw new ArgumentException();4   Func<int, int> handler = n => {5     if (n == 0) return 1;6     return n * handler(n - 1);7   };8   return handler(n);9 }

  这时handler(n-1) 的调用会报错(Use of unassigned local variable 'handler'),原因是handler还未被赋值。通常,这种问题我们会尝试用一种非常难看的做法变通解决:

1 Func<int, int> handler = null;2 handler = n =>{3   if (n == 0) return 1;4   return n * handler(n - 1);5 };

  咳咳咳,不多说了,心里一万只羊驼狂奔而过。

  2. 其次,Lambda表达式的使用,非常有局限,它不允许在参数中添加行为修饰 out, ref, params, 以及可选参数,均不能在Lambda表达式的参数表中出现。参数无法使用泛型。

  3. 分配了一个委托对象来指向函数,为了能够在Lambda表达式中访问本地变量,会为其分配一个新的对象,间接的增加了GC的压力。

 

说到这里,可能有的童鞋会自然的想到更原始的解决方案,在外部声明一个私有函数,就不会存在以上一系列的问题。

 1 class Foo 2 { 3   public void Bar(int[] arr) 4   { 5     foreach(var i in arr) 6     { 7       Console.WriteLine(Handler(i)); 8     } 9   }10   11   private string Handler(int i) => $"当前值是:{i}";12 }

这的确是一种简单粗暴的解决方案,但是依然存在一些小问题,一个仅在Bar方法中有引用的方法,逻辑上也没必要暴露给this的其他成员。这种做法其实是结构上的一种不合理。

 

本地方法(Local Function)

在C#7.0中,允许代码直接在一个方法体(方法,构造,属性的Getter和Setter)里声明并调用子方法。

废话不说,上代码:

 1 public void Bar(int[] arr) 2 { 3   var length = arr.Length; 4   string Length() { 5     return $"length is {length}"; 6   } 7   //或: 8   //string Length() => $"length is {length}"; 9   Length();10 }

上面例子中的Length(),在编译后会转化成当前类的私有成员方法(IL: this.<Bar>g__Length(): string()),但由于在C#语言层面做出了限制,只被允许在Bar方法中访问。

由于对this而言,是以类似匿名方法的形态存在,所以,在当前类中仍然可以定义同名且同样声明的成员方法,从所在的方法体中调用,会执行本地方法。

Okay,话说回来,由于它的本质成员方法,所以它可以避免 [委托 / Lambda表达式] 的种种限制,可以异步,可用泛型,可用out, ref, param, 可以yield, 特性参数, 等等。。

来个例子:

 1 class Foo 2 { 3   public IEnumerable<T> Bar<T>(params T[] items) 4   { 5     if (items == null) throw new ArgumentException(nameof(items)); 6  7     IEnumerable<T2> Enumerate<T2> ([CallerMemberName] T2[] array) //使用泛型及特性参数 8     { 9       //本地方法逻辑10       foreach (var item in array)11       {12         yield return item; //使用迭代器13       }14     }15 16     return Enumerate<T>(items); //调用本地方法17     //return this.Enumerate<T>(items); //调用成员方法18   }19 20   IEnumerable<T2> Enumerate<T2>([CallerMemberName] T2[] array)21   {22     //成员方法逻辑23   }24 }

 

总结

这个feature可以看出,C#在朝着函数式语言谨慎的调整。回想起很多人在首次接触代码的懵懂期,经常犯一种比较低级的错误,傻傻的尝试在Main方法中写函数声明,现在看来,那才是最直接的思维逻辑。

 

目前(2016年6月)C#7.0还未正式发布,大家如果想体验部分特性,可以去下载VS15预览版,最终发布的语法可能和本文中提及的有说不同,最新动态请大家关注Roslyn项目。




原标题:C# 7.0 新特性2: 本地方法

关键词:C#

C#
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

脑子瓦特?美国人要求中国偿还清朝的债券:https://www.ikjzd.com/articles/106440
选品分析:日出200单的午餐盒数据分解与选品参考!:https://www.ikjzd.com/articles/106441
实用!“两步申报”50个常见问题全面解答!:https://www.ikjzd.com/articles/106442
床单大市场!亚马逊床品大牌融资1亿美元!:https://www.ikjzd.com/articles/106443
中国蚊帐出海后被西班牙人誉为国家级法宝!:https://www.ikjzd.com/articles/106444
What!亚马逊抢夺第三方卖家订单?:https://www.ikjzd.com/articles/106445
长治婚庆女司仪和主持人:https://www.vstour.cn/a/366176.html
北京丰台区水上乐园哪家好玩?:https://www.vstour.cn/a/366177.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流