c#擴充套件方法,協程,dotween的仿寫
擴充套件方法。
引用msdn上的一句話:
“擴充套件方法使您能夠向現有型別“新增”方法,而無需建立新的派生型別、重新編譯或以其他方式修改原始型別。”
我們可以在沒有原始碼,不改變原始碼的基礎上,給基礎型別比如int上新增一些方法。
使用擴充套件方法的要求:
第一,實現擴充套件方法的類必須是靜態類且類的名稱和實現擴充套件方法的類無關;後一句大概是你要擴充套件int類,你靜態類名不能叫int吧。
第二、實現擴充套件方法的類方法必須是靜態方法;
第三、實現擴充套件方法的類方法的第一個引數必須是使用this關鍵字指明要實現擴充套件方法的類。
而C#支援擴充套件方法是從.NET3.5版本開始。
下面的程式碼擴充套件了int類,增加了求一個數的平方和一個數加另一個數的和的平方的方法,分別是傳引數和不傳引數的擴充套件方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using myspace;
namespace extended_method
{
class Program
{
static void Main(string[] args)
{
int k = 5, j = 3;
Console.WriteLine("k's square: " + k.getSquare());
Console.WriteLine("k plus j then square: " + k.plusToSquare(j));
}
}
}
namespace myspace
{
/// <summary>
/// 1、定義一個靜態類
/// 2、靜態類的名稱和要實現擴充套件方法的具體類無關
/// </summary>
/// <summary>
/// 3、實現一個具體的靜態方法
/// </summary>
/// <param name="n">4、第一個引數必須使用this關鍵字指定要使用擴充套件方法的型別</param>
/// <returns></returns>
public static class squareNum
{
public static int getSquare(this int n)
{
return n * n;
}
}
public static class plusANumbertoSquare
{
public static int plusToSquare(this int n,int k)
{
return (n + k) * (n + k);
}
}
}
yield,IEnumerable和IEnumerator
參考:http://www.cnblogs.com/vivid-stanley/p/5272736.html
C#語法中有個特別的關鍵字yield。
yield 是在迭代器塊中用於向列舉數物件提供值或發出迭代結束訊號。它的形式為下列之一:
yield return ;
yield break
IEnumerable的定義
namespace System.Collections
{
//
// 摘要:
// 公開列舉數,該列舉數支援在非泛型集合上進行簡單迭代。
[ComVisible(true)]
[Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
public interface IEnumerable
{
//
// 摘要:
// 返回一個迴圈訪問集合的列舉器。
//
// 返回結果:
// 可用於迴圈訪問集合的 System.Collections.IEnumerator 物件。
[DispId(-4)]
IEnumerator GetEnumerator();
}
}
轉到IEnumerator的定義,我們看到IEnumerator介面定義了一個Current屬性,MoveNext和Reset兩個方法。
using System.Runtime.InteropServices;
namespace System.Collections
{
//
// 摘要:
// 支援對非泛型集合的簡單迭代。
[ComVisible(true)]
[Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
public interface IEnumerator
{
//
// 摘要:
// 獲取集合中的當前元素。
//
// 返回結果:
// 集合中的當前元素。
object Current { get; }
//
// 摘要:
// 將列舉數推進到集合的下一個元素。
//
// 返回結果:
// 如果列舉數成功地推進到下一個元素,則為 true;如果列舉數越過集合的結尾,則為 false。
//
// 異常:
// T:System.InvalidOperationException:
// 在建立了列舉數後集合被修改了。
bool MoveNext();
//
// 摘要:
// 將列舉數設定為其初始位置,該位置位於集合中第一個元素之前。
//
// 異常:
// T:System.InvalidOperationException:
// 在建立了列舉數後集合被修改了。
void Reset();
}
}
有一個需要注意的地方:
最初,列舉數被定位於集合中第一個元素的前面。Reset 也將列舉數返回到此位置。在此位置,呼叫 Current 會引發異常。因此,在讀取 Current 的值之前,必須呼叫 MoveNext 將列舉數提前到集合的第一個元素。
IEnumerator支援對非泛型集合的簡單迭代,C#中,凡是實現了IEnumerator介面的資料型別都可以用foreach語句進行迭代訪問。
實現可迭代(可以用foreach)的資料集合,必須實現IEnumerable中的GetEmumerator()方法,返回實現了IEmumerator的物件例項。
完成集合的遍歷,可以使用yield return. yield return 需要配合IEmumerator進行使用,在外部foreach迴圈中,它會執行GetEmumerator()方法,遇到yield return, 做了如下兩件事情:
1.記錄下當前執行到的程式碼位置
2. 將程式碼控制權返回到外部, yield return 後面的值, 作為迭代的當前值。把返回的值轉化成IEmumerator,從而作為當前遍歷的值。
當執行下一個迴圈, 從剛才記錄的程式碼位置後面, 開始繼續執行程式碼。
然而我們怎麼控制yield return後面的程式碼的執行呢?
一個栗子:
public class myCollection : IEnumerable
{
public static void Main(string[] args)
{
myCollection mycol = new myCollection();
IEnumerator enumerator = mycol.GetEnumerator();
while (true)
{
bool canMoveNext = enumerator.MoveNext();
Console.WriteLine("canMoveNext:" + canMoveNext);
if (!canMoveNext)
break;
Object obj = enumerator.Current;
Console.WriteLine("current obj:" + obj);
}
Console.WriteLine("Main Function End.");
}
public IEnumerator GetEnumerator()
{
yield return "first task";
yield return "second";
yield return "third";
yield return "fourth";
Console.WriteLine("After all yield returns.");
}
}
執行,輸出的結果為:
canMoveNext:True
current obj:first task
canMoveNext:True
current obj:second
canMoveNext:True
current obj:third
canMoveNext:True
current obj:fourth
After all yield returns.
canMoveNext:False
Main Function End.
說明我們遍歷一個IEnumerator的時候,首先判斷MoveNext()是否為true,是的話就說明這個IEnumerator還沒遍歷完,我們就可以繼續遍歷,Current可以獲得yield return返回的物件,MoveNext()是false我們就遍歷完了。
那麼,我們就可以通過yield return 條件,把函式分割成很多段,每一段可以執行完一定任務後才繼續執行。當MoveNext()是false,意味著任務全部執行完。我們可以這樣來控制程式碼在特定的時候執行。
協程
函式是層級呼叫的,A呼叫B,B執行過程中呼叫C,C執行完畢返回,B執行完後返回,最後是A執行完畢。
子程式呼叫是通過棧實現的,子程式呼叫總是一個入口,一次返回,呼叫順序是明確的。而協程的呼叫和子程式不同。
協程看上去也是子程式,但執行過程中,在子程式內部可中斷,然後轉而執行別的子程式,在適當的時候再返回來接著執行。
Unity3d中的協程
Unity3d協程的格式:返回值是IEnumerator和擁有yield返回語句。這就不難理解了吧。
參考部落格:http://blog.csdn.net/huang9012/article/details/38492937
為了能在連續的多幀中呼叫該方法,Unity必須通過某種方式來儲存這個方法的狀態,這是通過IEnumerator 中使用yield return語句得到的返回值,當你“yield”一個方法時,效果等同於,“現在停止這個方法,然後在下一幀中或者某個條件滿足後從這裡重新開始!”。
UnityGems.com給出的協程的定義:
即協程是一個分部執行,遇到條件(yield return 語句)會掛起,直到條件滿足才會被喚醒繼續執行後面的程式碼。
協程不是多執行緒(儘管看上去很像),它們執行在同一執行緒中。
注意:用0或者null來yield的意思是告訴協程等待下一幀,直到執行完為止。當然,同樣的你可以繼續yield其他協程。
一般我們呼叫函式的時候,這個函式會在一幀的時間內完成。如果我們希望函式不在一幀裡完全完成,每次執行一部分,這時候就可以用到協程。
一個栗子,掛在一個Cube上,效果是每隔一秒,Console都會顯示一行新的輸出:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Coroutine : MonoBehaviour
{
void Start()
{
StartCoroutine(SayHello(5,1.0f));
}
IEnumerator SayHello(int times,float interval)
{
int seconds = 0;
for (int i = 0; i < times; i++)
{
for (float timer = interval; timer >= 0; timer -= Time.deltaTime)
{
yield return 0;
}
seconds++;
Debug.Log("Hello: " + seconds + "seconds have passed");
}
}
}
協程巢狀
一個栗子,如下指令碼:
第二個方法SayMessage用了yield,但它並沒有用0或者null,而是用了Wait()來yield,這相當於是說,“不再繼續執行本程式,直到Wait程式結束”。
效果是,程式1秒後輸出“after writing the last message, 1 second has passed”
1秒的2.5秒後輸出“after writing the last message, 2.5 seconds have passed”
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoroutinewithCoroutine : MonoBehaviour {
void Start () {
StartCoroutine(SaySomeThings());
}
IEnumerator Wait(float duration)
{
for (float timer = duration; timer >=0; timer -= Time.deltaTime)
yield return 0;
}
IEnumerator SaySomeThings()
{
Debug.Log("The Coroutine has started");
yield return StartCoroutine(Wait(1.0f));
Debug.Log("after writing the last message, 1 second has passed");
yield return StartCoroutine(Wait(2.5f));
Debug.Log("after writing the last message, 2.5 seconds have passed");
}
}
這才是真正要講的…仿寫Dotween
我的理解Dotween是一個擴充套件方法庫,可以擴充套件已有的transform類的方法,實現一些更高要求的動作,比如:transform.DoMove(vec3,time)可以實現在time時間內移動到vec3位置的動畫效果。
dotween物件
dotween物件對應的是對應動作的協程。來控制對應協程,通過改變成員變數中的isPause和autoKill可以決定對應的協程是否暫停,dotween物件是否在執行完之後自動銷燬。在擴充套件方法中,每次呼叫transform的擴充套件方法,就會產生一個對應的dotween,和一個協程。dotween在構造的時候會自動新增到Dotween工廠的列表中管理。
public class dotween {
public string name;
public Coroutine coroutine = null;
public Transform transform = null;
public Vector3 target;
public float time;
public bool isPause = false;
public bool autoKill = true;
public delegate void CallBackFunc();
public CallBackFunc callbackFunc = null;
public dotween(string dotname,Transform nowtrans,Vector3 tar,float duration,Coroutine co)
{
name = dotname;
transform = nowtrans;
target = tar;
time = duration;
//新增到工廠中管理
DotweenFactory.getInstance().Add(this);
}
public void setCoroutine(Coroutine co)
{
coroutine = co;
}
public void Kill()
{
MonoBehaviour mono = transform.GetComponent<MonoBehaviour>();
mono.StopCoroutine(coroutine);
//結束協程,從工廠中刪除
DotweenFactory.getInstance().Remove(this);
}
public void Play()
{
isPause = false;
}
public void Pause()
{
isPause = true;
}
//設定回撥函式,可以設定多個回撥函式
public void AddCallBackFunc(CallBackFunc func)
{
callbackFunc += func;
}
public void runCallBackFunc()
{
if (callbackFunc != null)
{
callbackFunc();
}
if (autoKill)
{
Kill();
}
}
//設定執行完動作後是否自動銷燬
public void setAutoKill(bool b)
{
autoKill = b;
}
}
Dotween工廠
Dotween工廠管理所有的dotween,把dotween儲存在列表中,可以讓同一名字的動作一起暫停,播放,銷燬。又或者讓一個物件的所有動作停止,播放,銷燬。
public class DotweenFactory {
private static List<dotween> dotweenList = new List<dotween>();
private static DotweenFactory dotFac = null;
public static DotweenFactory getInstance()
{
if (dotFac==null)
{
dotFac = new DotweenFactory();
}
return dotFac;
}
//新增一個dotween物件到list中
public void Add(dotween dot)
{
dotweenList.Add(dot);
}
//從list中刪除一個dotween物件
public void Remove(dotween dot)
{
dotweenList.Remove(dot);
}
//暫停
public static void PauseAll()
{
for (int i = dotweenList.Count - 1; i >= 0; i--)
{
dotweenList[i].Pause();
}
}
public static void Pause(string name)
{
for (int i = dotweenList.Count - 1; i >= 0; i--)
{
if (dotweenList[i].name == name)
{
dotweenList[i].Pause();
}
}
}
public static void Pause(Transform transform)
{
for (int i=dotweenList.Count-1;i>=0;i--)
{
if (dotweenList[i].transform==transform)
{
dotweenList[i].Pause();
}
}
}
//播放
public static void PlayAll()
{
foreach (dotween dot in dotweenList)
{
dot.Play();
}
}
public static void Play(string name)
{
foreach (dotween dot in dotweenList)
{
if (dot.name == name)
{
dot.Play();
}
}
}
public static void Play(Transform transform)
{
foreach (dotween dot in dotweenList)
{
if (dot.transform == transform)
{
dot.Play();
}
}
}
//銷燬
public static void KillAll()
{
for (int i = dotweenList.Count - 1; i >= 0; i--)
{
dotweenList[i].Kill();
}
}
public static void Kill(string name)
{
for (int i = dotweenList.Count - 1; i >= 0; i--)
{
if (dotweenList[i].name == name)
{
dotweenList[i].Kill();
}
}
}
public static void Kill(Transform transform)
{
for (int i = dotweenList.Count - 1; i >= 0; i--)
{
if (dotweenList[i].transform == transform)
{
dotweenList[i].Kill();
}
}
}
}
擴充套件方法
建立dotween物件,建立協程,給dotween設定它控制的協程,在協程中確認dotween狀態,決定是否暫停等,動作結束後呼叫回撥函式。
public static class extension_method{
//DoMove擴充套件方法(MonoBehaviour,Transform)
//通過time秒移動到指定的位置target
public static IEnumerator DoMove(this MonoBehaviour mono,dotween dot)
{
//目標和現在的位置之差除以時間算出速度
Vector3 distance = (dot.target - dot.transform.position) / dot.time;
for (float f = dot.time; f >= 0.0f; f -= Time.deltaTime)
{
dot.transform.Translate(distance * Time.deltaTime);
yield return null;
//根據dotween確認是否暫停
while (dot.isPause == true)
{
yield return null;
}
}
//動作結束呼叫回撥函式
dot.runCallBackFunc();
}
public static dotween DoMove(this Transform transform,Vector3 target,float time)
{
MonoBehaviour mono = transform.GetComponents<MonoBehaviour>()[0];
//新建dotween
dotween dot = new dotween("DoMove", transform, target, time, null);
//建立協程
Coroutine coroutine = mono.StartCoroutine(mono.DoMove(dot));
//給dotween設定對應協程
dot.setCoroutine(coroutine);
return dot;
}
//DoScale擴充套件方法(MonoBehaviour,Transform)
//time秒之內放大到特定大小target
public static IEnumerator DoScale(this MonoBehaviour mono, dotween dot)
{
Vector3 distance = (dot.target - dot.transform.localScale) / dot.time;
for (float f = dot.time; f >= 0.0f; f -= Time.deltaTime)
{
dot.transform.localScale = dot.transform.localScale + distance * Time.deltaTime;
yield return null;
while (dot.isPause == true)
{
yield return null;
}
}
dot.runCallBackFunc();
}
public static dotween DoScale(this Transform transform, Vector3 target, float time)
{
MonoBehaviour mono = transform.GetComponents<MonoBehaviour>()[0];
dotween dot = new dotween("DoScale", transform, target, time, null);
Coroutine coroutine = mono.StartCoroutine(mono.DoScale(dot));
dot.setCoroutine(coroutine);
return dot;
}
//DoPause擴充套件方法(MonoBehaviour,Transform)
//time秒之後暫停所有動作
public static IEnumerator DoPause(this MonoBehaviour mono, dotween dot)
{
for (float f = dot.time; f >= 0.0f; f -= Time.deltaTime)
{
yield return null;
while (dot.isPause == true)
{
yield return null;
}
}
DotweenFactory.PauseAll();
Debug.Log("pause");
}
public static dotween DoPause(this Transform transform, float time)
{
MonoBehaviour mono = transform.GetComponents<MonoBehaviour>()[0];
dotween dot = new dotween("DoPause", transform, transform.position, time, null);
Coroutine coroutine = mono.StartCoroutine(mono.DoPause(dot));
dot.setCoroutine(coroutine);
return dot;
}
}
使用
寫完擴充套件方法之後,就可以在指令碼中呼叫對應的擴充套件方法,輕鬆實現你想要的運動效果。
比如,前3秒移動,前4.5秒變大,4.5秒的時候停止動作。
public class test : MonoBehaviour {
dotween dot;
// Use this for initialization
void Start () {
dot = transform.DoMove(new Vector3(3, 3, 3), 3.0f);
transform.DoScale(new Vector3(3, 3, 3), 6.0f);
transform.DoPause(4.5f);
dot.AddCallBackFunc(func);
}
void func()
{
Debug.Log("action complete");
}
// Update is called once per frame
void Update () {
}
}
效果圖:
相關推薦
c#擴充套件方法,協程,dotween的仿寫
擴充套件方法。 引用msdn上的一句話: “擴充套件方法使您能夠向現有型別“新增”方法,而無需建立新的派生型別、重新編譯或以其他方式修改原始型別。” 我們可以在沒有原始碼,不改變原始碼的基礎上,給基礎型別比如int上新增一些方法。 使用擴充套件方法的
進程,線程,協程,異步IO知識點
variables down mas sock pipe unique soc read 但是 進程: qq 要以一個整體的形式暴露給操作系統管理,裏面包含對各種資源的調用,內存的管理,網絡接口的調用等。。。對各種資源管理的集合 就可以成為 進程線程: 是操作系統最小的調
執行緒,程序,協程,非同步和同步,非阻塞IO
1.執行緒,程序,協程 程序定義:程序是具有一定獨立功能的程式在一個數據集上的一次動態執行的過程,是系統進行資源分配和排程的一個獨立單位 執行緒定義:執行緒是CPU排程和分派的基本單位,是比程序更小能獨立執行的單位,執行緒佔有系統。但是它可以與它同屬的程序和其他在該程序中的執行緒共享
程序,執行緒,協程,io多路複用 總結
併發:要做到同時服務多個客戶端,有三種技術 1. 程序並行,只能開到當前cpu個數的程序,但能用來處理計算型任務 ,開銷最大 2. 如果並行不必要,那麼可以考慮用執行緒併發,單位開銷比程序小很多 執行緒:併發(輪詢排程,遇到阻塞就切換) 只要是網路,就會有延遲,有延遲就阻塞,所以比
進程,線程,協程,io多路復用 總結
協程 很多 能開 同時 計算 多個 調度 耗資源 會有 並發:要做到同時服務多個客戶端,有三種技術 1. 進程並行,只能開到當前cpu個數的進程,但能用來處理計算型任務 ,開銷最大 2. 如果並行不必要,那麽可以考慮用線程並發,單位開銷比進程小很多 線程:並發(
Cpython的程序,執行緒,協程,io模型
一、程序: 一 基礎概念: 1、程序的定義: 程序本身是一個抽象的概念,程序是作業系統資源分配的基本單位,是正在進行的一個過程或者說一個任務,負責執行任務的是cpu。 2、並行與併發: 假設計算機只有一個cpu,由於一個cpu在同一時間只能執行一個任務,那麼當有多
進程,線程,協程的區別
概念 虛擬 每次 計數器 python 對比 bsp 系統資源 句柄 一、概念 1、進程 進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。每個進程都有自己的獨立內存空間,不同進程通過進程間通信來通信。由於進程比較
協程,異步IO
com https 簡化 opened hide str pen pre star 協程 協程,又稱微線程,纖程。英文名Coroutine。一句話說明什麽是線程:協程是一種用戶態的輕量級線程。實現單線程的並發。 協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文
Swoole 2.0 正式版發布,協程特性支持 PHP 7
好的 str 圖片 amp quest CP nbsp fail 開發者 Swoole 2.0正式版發布了。2.0版本最大的更新是增加了對協程(Coroutine)的支持。正式版已同時支持PHP5和PHP7。基於Swoole2.0協程PHP開發者可以已同步的方式編寫代碼
異步調用與回調機制,協程
線程等待 AC 2-2 非阻塞 一次 ont 自帶 finally 流程 1.異步調用與回調機制 上一篇我們已經了解到了兩組比較容易混淆的概念問題,1.同步與異步調用 2.阻塞與非阻塞狀態。在說到異步調用的時候,說到提交任務後,就直接執行下一行代碼,而不去拿結果,這樣明
玩轉python(7)python多協程,多線程的比較
用戶體驗 time() cut 過程 RR 至少 執行 結果 關鍵字 前段時間在做一個項目,項目本身沒什麽難度,只是數據存在一個數據接口服務商那兒,這就意味著,前端獲取數據需要至少兩次http請求,第一次是前端到後端的請求,第二次是後端到數據接口的請求。有時,後端接收到前端
異步IO(協程,消息循環隊列)
http框架 for 例如 高並發 bsp 標識 類型 tex 請求 同步是CPU自己主動查看IO操作是否完成,異步是IO操作完成後發出信號通知CPU(CPU是被通知的) 阻塞與非阻塞的區別在於發起IO操作之後,CPU是等待IO操作完成再進行下一步操作,還是不等待去做其他的
python-進程池與線程池,協程
接收 另一個 cep 用戶 大於 some don 多個 接口 一、進程池與線程池 實現並發的手段有兩種,多線程和多進程。註:並發是指多個任務看起來是同時運行的。主要是切換+保存狀態。 當我們需要執行的並發任務大於cpu的核數時,我們需要知道一個操作系統不能無限的開啟進
python 多進程,多線程,協程
task return time finall ssi args 並且 python 代碼 在我們實際編碼中,會遇到一些並行的任務,因為單個任務無法最大限度的使用計算機資源。使用並行任務,可以提高代碼效率,最大限度的發揮計算機的性能。python實現並行任務可以有多進程,多
python 多執行緒, 多程序, 協程
1. 介紹: threading用於提供執行緒相關的操作,執行緒是應用程式中工作的最小單元。python當前版本的多執行緒庫沒有實現優先順序、執行緒組,執行緒也不能被停止、暫停、恢復、中斷。 2. 1 執行緒執行函式 #!/bin/python #coding:utf8 import
多程序,協程
多程序 並行,同時執行。 一個程式執行起來之後,程式碼用到的資源稱之為程序,它是作業系統分配資源的基本單位,不僅可以通過執行緒完成多工,程序也是可以的 程序之間是相互獨立的 cpu計算密集的時候適合用多程序 有幾核CPU就可以同時執行幾個程序 開啟程序需要呼叫 multiprocessing 模組
Python 協程,gevent(yield阻塞,greenlet),協程實現多工(有規律的交替協作執行)
實現多工:程序消耗的資源最大,執行緒消耗的資源次之,協程消耗的資源最少(單執行緒)。 gevent實現協程,gevent是通過阻塞程式碼(例如網路延遲等)來自動切換要執行的任務,所以在進行IO密集型程式時(例如爬蟲),使用gevent可以提高效率(有效利用網路延遲的時間去執行其他任務)。 &
一段小程式淺析Go中的併發,協程(goroutine),sync.WaitGroup
package main import ( "fmt" "runtime" "sync" ) func main() { runtime.GOMAXPROCS(2) fmt.Println("begin typing") var wg sync.WaitGro
python併發,協程
在作業系統中程序是資源分配的最小單位, 執行緒是CPU排程的最小單位。 協程:是單執行緒下的併發,又稱微執行緒,纖程。英文名Coroutine。一句話說明:協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的。也就是說程式設計師用程式碼來控制切換. 參考: http://www.cnblo
單執行緒實現併發——協程,gevent模組
一 併發的本質 1 切換 2 儲存狀態 二 協程的概念 協程,又稱微執行緒,纖程。英文名Coroutine。單執行緒下實現併發,使用者從應用程式級別控制單執行緒下任務的切換,注意一定是遇到I/O才切。 協程的特點在於是一個執行緒執行,那和多執行緒比,協程有何優勢?