L#是什麼:Run DLL as a Script.
A Pure C# IL Runner,直接解析執行IL的指令碼引擎。
從原理上講是模擬執行了CLR的工作,從表現上講就是把DLL作為資源直接載入執行。
是不是很多同學夢寐以求的熱更DLL?能熱更,但不是你想的那個熱更。
直接以反射符號的方式載入DLL,DLL的解釋執行還是由CLR完成的。還會涉及JIT引擎。
但是L#是"模擬"CLR的工作。不使用反射載入符號,不使用JIT
在IOS(Unity)、WP8這樣的平臺,CLR就沒反射載入符號這樣的功能。IOS還關閉了JIT
L#不受這個制約
但是就帶來了其他制約:互動上的一些麻煩
主要記住這一條:L#載入的DLL中的程式碼屬於指令碼程式碼,和系統程式碼中的程式不能互相繼承
L#的語法是什麼樣的:
L# 是直接執行dotnet 的DLL, 你可以用c# vb.net f# 等。只要能通過編譯。
與C#Light自己實現語法解釋問題不同,L#沒有語法解釋層面的bug,只會有執行層面的bug。
恰恰是C#Light在語法直譯器的維護上陷入泥潭以後,深刻反思,才有了L#這個創意。
關於程式碼:
可以在http://svn.cltri.com 取得最新程式碼
或者https://github.com/lightszero/lsharp
目前還處於Alpha階段,Bug是少不了的,這個階段僅建議動手能力比較強的同學試用。
發現BUG罵我是肯定要罵的,如果罵完之後,能夠在Github上推一個錯誤用例
將不甚感激。
下面介紹一下L#在Unity3D的使用辦法
一步一步是這樣,是魔鬼的步伐
L#ForUnity3D Hell#oworld環境建立圖解
1.建立新專案
2.Copy L#forU3D的類庫到DLL中
或者直接插入L#forU3D的原始碼也可以,自選
3.建立一個程式碼供L#模組訪問)
剽竊了龐麥郎的歌詞,但我是不會道歉的
4.建立一個L#模組
5.新增L#模組的引用
因為我們剛才在plugins目錄下建立的程式碼Interface.cs,希望L#模組訪問他,就對應Assembly-Csharp-firstpass這個模組
6.編寫L#模組
可以看到L#模組粗獷的呼叫了我們的程式
然後F7,你會得到L#模組的dll檔案
7.把模組檔案作為資源放進Unity3D
把模組檔案改名為
HotFixCode.Dll.bytes
HotFixCode.Pdb.bytes
pdb這個檔案不是必須的,但是當指令碼出異常,你需要查一下是哪個檔案哪一行出錯的時候,沒有pdb是查不出來的。只能給你一個乾巴巴的地址。
當然你熟悉IL的話,自己用別的工具,根據這個地址也是可以找到程式碼位置的。
他在Unity裡面應該被識別為兩個TextAsset
8.編寫測試程式Test001
新建場景並儲存Test001,
新建Test001指令碼直接掛接到攝像機上
編寫程式碼如下:
using UnityEngine;
using System.Collections;
public class test001 : MonoBehaviour {
CLRSharp.CLRSharp_Environment env ;
// Use this for initialization
void Start () {
//建立CLRSharp環境
env=new CLRSharp.CLRSharp_Environment(new Logger());
}
// Update is called once per frame
void Update () {
}
public class Logger:CLRSharp.ICLRSharp_Logger//實現L#的LOG介面
{
public void Log (string str)
{
Debug.Log (str);
}
public void Log_Error (string str)
{
Debug.LogError (str);
}
public void Log_Warning (string str)
{
Debug.LogWarning (str);
}
}
}
9.執行HelloWorld
進入Unity,執行專案
這是唯一的結果,有一條Log L#被初始化了
L#HelloWorld 第二步,呼叫L#模組圖解
繼續增加程式碼
在Start函式後增加程式碼
0.載入模組
//載入L#模組
TextAsset dll = Resources.Load("HoxFixCode.dll") as TextAsset;
TextAsset pdb = Resources.Load("HoxFixCode.pdb") as TextAsset;
System.IO.MemoryStream msDll = new System.IO.MemoryStream (dll.bytes);
System.IO.MemoryStream msPdb = new System.IO.MemoryStream (pdb.bytes);
//env.LoadModule (msDll, null);//不需要pdb的話,第二個引數傳null
env.LoadModule (msDll, msPdb);
1.建立執行緒上下文
2.取得準備呼叫的型別
3.呼叫靜態函式
4.呼叫成員函式
完整的start函式程式碼
void Start () {
//建立CLRSharp環境
env=new CLRSharp.CLRSharp_Environment(new Logger());
//載入L#模組
TextAsset dll = Resources.Load("HoxFixCode.dll") as TextAsset;
TextAsset pdb = Resources.Load("HoxFixCode.pdb") as TextAsset;
System.IO.MemoryStream msDll = new System.IO.MemoryStream (dll.bytes);
System.IO.MemoryStream msPdb = new System.IO.MemoryStream (pdb.bytes);
//env.LoadModule (msDll, null);//不需要pdb的話,第二個引數傳null
env.LoadModule (msDll, msPdb);
Debug.Log ("LoadModule HotFixCode.dll done.");
//step01建立一個執行緒上下文,用來模擬L#的執行緒模型,每個執行緒建立一個即可。
CLRSharp.ThreadContext context = new CLRSharp.ThreadContext (env);
Debug.Log ("Create ThreadContext for L#.");
//step02取得想要呼叫的L#型別
CLRSharp.ICLRType wantType = env.GetType ("HoxFixCode.TestClass",null);//用全名稱,包括名稱空間
Debug.Log ("GetType:"+wantType.Name);
//和反射程式碼中的Type.GetType相對應
//step03 靜態呼叫
//得到型別上的一個函式,第一個引數是函式名字,第二個引數是函式的參數列,這是一個沒有引數的函式
CLRSharp.IMethod method01 = wantType.GetMethod("Test1",CLRSharp.MethodParamList.MakeEmpty());
method01.Invoke (context, null, null);//第三個引數是object[] 參數列,這個例子不需要引數
//這是個靜態函式呼叫,對應到程式碼他就是HotFixCode.TestClass.Test1();
//step04 成員呼叫
//第二個測試程式是一個成員變數,所以先要建立例項
CLRSharp.CLRSharp_Instance typeObj = new CLRSharp.CLRSharp_Instance (wantType as CLRSharp.ICLRType_Sharp);//建立例項
CLRSharp.IMethod methodctor = wantType.GetMethod(".ctor",CLRSharp.MethodParamList.MakeEmpty());//取得建構函式
methodctor.Invoke (context, typeObj, null);//執行建構函式
//這幾行的作用對應到程式碼就約等於 HotFixCode.TestClass typeObj =new HotFixCode.TestClass();
CLRSharp.IMethod method02 = wantType.GetMethod("Test2",CLRSharp.MethodParamList.MakeEmpty());
method02.Invoke (context, typeObj, null);
//這兩行的作用就相當於 typeOBj.Test2();
}