C# 知识体系构建| | C# 3.0 上

Posted 小小Unity

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# 知识体系构建| | C# 3.0 上相关的知识,希望对你有一定的参考价值。

5.1 C# 3.0

本章我们开始C# 3.0的学习。先贴出C# 3.0新增的语法特性,如下:

  • 自动实现的属性

  • 匿名类型

  • 查询表达式(LINQ)

  • 表达式

  • 表达式树

  • 扩展方法

  • 隐式类型本地变量

  • 分部方法

  • 对象和集合初始值设定项

可以说到了C# 3.0,C#才开始有一些突出的亮点。在以上的特性中,最需要掌握的是LINQ和扩展方法,其它都是一些小的语法细节。所以本节的重点学习内容就是LINQ和扩展方法。

在Unity 5.x和2017.x版本中,C# 3.0是主要的语言版本,当然也还支持了一部分C# 4.0的特性。C# 3.0的特性需要开发者花较长的时间进行学习和掌握,不过一旦掌握,就可以被称为合格的C#使用者了。

接下来,我们就开始学习C# 3.0的第一个特性。

5.2 C# 3.0快速浏览

5.2.1 自动实现属性

这个特性非常简单,代码如下:

public class PropertyExample {  // C# 3.0之前  private string mNickName;  public string NickName  {  get { return mNickName; }  set { mNickName = value; }  }  // C# 3.0  public string Name { get; set; } }

5.2.2 匿名类型

代码如下:

var person = new {Title = "Name"}; Debug.Log(person.Title);

本来接着介绍的应该是查询表达式,不过这部分内容之后再介绍,我们先介绍Lambda表达式。

5.2.3 Lambda表达式

Lambda表达式非常简单,就是匿名方法的一种表现,如下:

using System; public class LambdaExpressionExample {  void Func()  {  }  void Main()  {  // C# 1.0  Action action1 = new Action(Func);  // C# 2.0  Action actionB = delegate { };  // C# 3.0  Action acionC = () => { };  }}

5.2.4 表达式树

表达式树实际上是Expression这个API的使用,先看下代码,如下:

using System; using System.Linq.Expressions; using UnityEngine; namespace Master {  public class ExpressionTreeExample : MonoBehaviour  {  void Start()  {  // 创建表达式树  Expression> expr = num => num < 5;  // 将表达式树编译为一个委托  Func result = expr.Compile();  // 执行表达式树的委托  Debug.Log(result(4));  }  } } // 输出结果 // True

通过表达式树可以构造一个委托出来,当执行委托的时候,就执行表达式树所表达的代码。

以上这段代码是通过Lambda表达式来构建的表达式树,而最原始的表达式树的创建过程是通过Expression提供的API一个一个构建的,代码如下:

using System; using System.Linq.Expressions; using UnityEngine; namespace Master {  public class ExpressionTreeExample : MonoBehaviour  {  void Start()  {  // 创建一个 num 参数  var num = Expression.Parameter(typeof(int), "num");  // 创建一个 常数 5  var five = Expression.Constant(5, typeof(int));  // 创建一个表达式 num < 5  var numLessThan5 = Expression.LessThan(num, five);  // 创建一个 labmda 表达式 (num)=>num < 5 
var labmda = Expression.Lambda>(numLessThan5, new ParameterExpression[] { num }); // 将表达式树编译为一个委托 Func result = labmda.Compile(); // 执行表达式树的委托 Debug.Log(result(4)); } } } // 输出结果 // True

以上的构建过程同Lambda形式的构建过程类似。这里先只做一个简短的入门介绍,表达式树非常适合做方法参数验证等工作

5.2.5 扩展方法

扩展方法也非常简单,代码如下:

using UnityEngine; namespace Master {  public class StaticExtensionsExample : MonoBehaviour {  void Start()  { this.Show();  } }  ///  /// 定义  /// 需要是静态类  ///  public static class MonoExtensions  {  ///  /// ///  /// 需要有 this 关键字  public static void Show(this MonoBehaviour self)  {  self.gameObject.SetActive(true);  }  } }

关于静态方法,我会在之后进行一些经验的分享。接下来是隐式类型本地变量。

5.2.6 隐式类型本地变量

using UnityEngine; namespace Master {  public class StaticExtensionsExample : MonoBehaviour  {  void Start()  {  // 隐式 (使用 var)  var age = 10;  // 显式  int age2 = 10;  }  } }

5.2.7 分部方法

using UnityEngine; namespace Master {  public class StaticExtensionsExample : MonoBehaviour  {  void Start()  {  var obj = new TestPartialMethod();  obj.Click();  }  }  public partial class TestPartialMethod  {  public void Click(){ OnClick();}  ///  /// 不能有访问权限,实际上是 private 权限  ///  partial void OnClick();  }  public partial class TestPartialMethod  {  partial void OnClick()  {  Debug.Log("OnClick");  }  } } // 输出结果 // OnClick

5.2.8 对象和集合初始值设定项

using System.Collections.Generic; using UnityEngine; namespace Master {  public class InitialExample : MonoBehaviour  {  void Start()  {  var obj = new SomeClass() {Age = 10};  var students = new List() {"凉鞋", "匠人"};  var dictionary = new Dictionary()  { {"A", "Apple"}, {"B", "Banana"}, };  }  }  public class SomeClass  {  public int Age { get; set; }  } }

其中的一些内容需要重点介绍,比如查询表达式和扩展方法。

5.2.9 知识地图

5.3 扩展方法与Fluent API

这些特性中应该重点介绍的特性如下:

  • 查询表达式

  • 扩展方法

其它的特性,有的不常用(表达式树),有的比较容易掌握,就不详细介绍了。

我在贴出快速浏览C# 3.0时,扩展方法的示例代码,如下:

using UnityEngine; namespace Master {  public class StaticExtensionsExample : MonoBehaviour {  void Start()  {  this.Show();  }  }  ///  /// 定义  /// 需要是静态类  ///  public static class MonoExtensions  {  ///  ///  ///  /// 需要有 this 关键字  public static void Show(this MonoBehaviour self)  {  self.gameObject.SetActive(true);  }  }}

代码很简单,那为什么要重点介绍扩展方法这个特性呢?

因为扩展方法是C#的特色概念,也就是说C#有,但是别的语言没有(最近用于Flutter的Dart支持了扩展方法),这样让很多在C#容易做的事情,在其它语言中实现就非常困难。

扩展方法可以让我们对现有的对象增加方法,不用去修改对象类的代码、编译等,它本质上是一个静态方法。在C#中,最常见的扩展方法就是LINQ。对于Unity开发者来说,第一次接触扩展方法,应该是在使用DOTween的时候。我在做Unity之前, 有一些C++和Java的基础,但在接触到DOTween之后才感觉到C#的神奇之处。

DOTween的示例代码如下:

transform.DOMove(new Vector3(2,2,2), 2) .SetEase(Ease.OutQuint); 

就是从头点到尾的语法。不用扩展方法也是可以做到以上这样的链式编程的,但是有了扩展方法,会更容易实现链式的API,可以让每个类各司其职。

关于链式编程就先介绍到这里,接下来分享下我常用的扩展方法编程经验。

5.3.1 经验分享

传统用法:扩展对象的方法

最初我用扩展方法是为了简化Unity的API。简化的原因很简单,我做Unity之前是做Cocos开发的,已经习惯了Cocos的API风格,当时开发项目的阻碍就是在Cocos API的习惯上,通过再封装的方式重命名Unity的API,比如:

public static class BehaviourExtensions {  public static GameObject Show(this GameObject selfObj)  {  selfObj.SetActive(true);  return selfObj;  }  public static T Show(this T selfComponent) where T : Component  {  selfComponent.gameObject.Show(); return selfComponent;  } } // 使用方式: // this.Show(); // this 是 MonoBehaivour

开始接触Unity的时候,不知道扩展方法这个特性。我实现Show和Hide是在一个名为QMonoBehaviour的基类里实现的,如果想使用到Show和Hide这样的API,就需要继承QMonoBehaviour。后来我看了一下DOTween的实现,再加上当时的 leader提点,我逐步知道了扩展方法。这个扩展方法是我体验到的C#的第二个魅力,第一个魅力是委托和Lambda表达式的使用。

以上是非常简单的使用方式,注意这个API,是支持链式的。大部分的扩展方法都是用于设计一些API,或者扩展一些API,比如 .Net Core的Service注册或者Middleware的注册第三方库,一般都是用扩展方法实现的,这样做的好处就是用一行代码开 启某某插件、某某功能。对于经常设计API的开发者来说,扩展方法是一个非常好的方式。

第二种扩展方法的使用方式,就是链式的API。

5.3.2 基础的链式API

代码如下:

using UnityEngine; namespace QFramework.Master {  public class ExtensionsExample : MonoBehaviour  {  // 不用扩展方法的方式定义  public ExtensionsExample SayHello()  {  Debug.Log("Hello");  return this;  }  public ExtensionsExample SayGoodBye()  {  Debug.Log("Goodbye");  return this;  }  void Start()  {  this.SayHello().DoSomething().SayGoodBye();  } }  ///  /// 扩展方法的方式定义  ///  public static class MonoExtensions  {  ///  /// 使用泛型约束  ///  ///  ///  ///  public static T DoSomething(this T self) where T : MonoBehaviour  {  self.name = "DoSomething";  return self;  }  } }

以上的这种链式API是非常简单的实现,链式的API的核心就是返回自己(return this),可以把API写成无法使用的样式,也可以用泛型约束做成稍微通用一点的链式API。

第三种就是Fluent API,即流式的API,或者说有阶段的API。

5.3.3 Fluent API

其实以上的两种扩展方法使用方式都属于Fluent API范畴,只不过这些API还不够Fluent。

Fluent API由两名外国人提出,这里就不介绍了。我想介绍是有阶段的链式API。什么是有阶段的链式API?举个例子,代码如下:

using UnityEngine; namespace QFramework.Master {  public interface IStageStart : IStageA { }  public interface IStageA : IStageB { }  public interface IStageB : IStageBase { }  public interface IStageBase { void ExecuteStage(); } public static class StageExtensions  {  public static IStageA EnterStageA(this T self) where T : IStageStart { return self; }  public static IStageB EnterStageB(this T self) where T : IStageA { return self; } }  public class ExtensionsExample : MonoBehaviour, IStageStart  {  void Start()  {  this.EnterStageA().EnterStageB().ExecuteStage(); this.EnterStageB() .ExecuteStage(); this.ExecuteStage();  // 这个会报编译错误 因为顺序不对  // this.ExecuteStage() // .EnterStageB();  }  public void ExecuteStage() { } } }
代码需要仔细看一下,核心就是通过泛型约束和接口的继承,来强制让API按照一定的顺序调用。以上这个Fluent API的实现是我自己总结的,实际上还有一些其它Fluent API实现的方式,因为我没有用过所以就不介绍了。

对于扩展方法的使用,以上就是我认为最复杂的使用方式了。

5.3.4 建造者模式(Builder Pattern)

我在翻看别人写的源码时见过把Fluent API结合建造者模式(Builder)一起使用,不过这部分我没有尝试过,大家如果感兴趣,可以自己来试试实现一个这样的结构。

5.3.5 知识地图


以上是关于C# 知识体系构建| | C# 3.0 上的主要内容,如果未能解决你的问题,请参考以下文章

c# WinForm 全局键盘事件怎么监听。。。。会的童鞋们贴上代码和用例吧,感激

c#代码片段快速构建代码

一个C# (队列多任务+多线程处理)对象的winform demo

C# 3.0 自动属性——有用与否? [关闭]

使用 C# 3.0 编译的 C# 2.0 代码使用程序集

C#后台架构师成长之路-高阶知识体系核心