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 全局键盘事件怎么监听。。。。会的童鞋们贴上代码和用例吧,感激