我建议ILRuntime的官方手册作者罚抄《CLR via C#》100遍,看看人家怎么写教程的。


"scopedRegistries": [
{
"name": "ILRuntime",
"url": "https://registry.npmjs.org",
"scopes": [
"com.ourpalm"
]
}
],











(对应Examples 03)
//同一参数组合只需要注册一次 delegate void SomeDelegate(int a, float b); Action<int, float> act; //注册,不带返回值,最多支持五个参数传入 appDomain.DelegateManager.RegisterMethodDelegate<int, float>(); //注册,带参数返回值,最后一个参数为返回值,最多支持四个参数传入 delegate bool SomeFunction(int a, float b); Func<int, float, bool> act;
app.DelegateManager.RegisterDelegateConvertor<SomeFunction>((action) =>
{
return new SomeFunction((a, b) =>
{
return ((Func<int, float, bool>)action)(a, b);
});
});
public static StackObject* CreateInstance(ILIntepreter intp, StackObject* esp, List<object> mStack, CLRMethod method, bool isNewObj)
{
//获取泛型参数<T>的实际类型
IType[] genericArguments = method.GenericArguments;
if (genericArguments != null && genericArguments.Length == 1)
{
var t = genericArguments[0];
if (t is ILType)//如果T是热更DLL里的类型
{
//通过ILRuntime的接口来创建实例
return ILIntepreter.PushObject(esp, mStack, ((ILType)t).Instantiate());
}
else
return ILIntepreter.PushObject(esp, mStack, Activator.CreateInstance(t.TypeForCLR));//通过系统反射接口创建实例
}
else
throw new EntryPointNotFoundException();
}
//注册
foreach (var i in typeof(System.Activator).GetMethods())
{
//找到名字为CreateInstance,并且是泛型方法的方法定义
if (i.Name == "CreateInstance" && i.IsGenericMethodDefinition)
{
//RegisterCLRMethodRedirection:通过redirectMap存储键值对MethodBase-CLRRedirectionDelegate,如果i不为空且redirectMap中没有传入的MethodBase(即下方的i)则存储redirectMap[i] = CreateInstance。所以如此看来注册行为就是把键值对存储到redirectMap的过程
appdomain.RegisterCLRMethodRedirection(i, CreateInstance);
}
}
public unsafe static StackObject* DLog(ILIntepreter __intp, StackObject* __esp, List<object> __mStack, CLRMethod __method, bool isNewObj)
{
ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;
StackObject* ptr_of_this_method;
//只有一个参数,所以返回指针就是当前栈指针ESP - 1
StackObject* __ret = ILIntepreter.Minus(__esp, 1);
//第一个参数为ESP -1, 第二个参数为ESP - 2,以此类推
ptr_of_this_method = ILIntepreter.Minus(__esp, 1);
//获取参数message的值
object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);
//需要清理堆栈
__intp.Free(ptr_of_this_method);
//如果参数类型是基础类型,例如int,可以直接通过int param = ptr_of_this_method->Value获取值,
//关于具体原理和其他基础类型如何获取,请参考ILRuntime实现原理的文档。
//通过ILRuntime的Debug接口获取调用热更DLL的堆栈
string stackTrace = __domain.DebugService.GetStackTrance(__intp);
Debug.Log(string.Format("{0}\n{1}", format, stackTrace));
return __ret;
}
object[]数组,这样不可避免的每次调用都会产生不少GC Alloc。众所周知GC Alloc高意味着在Unity中执行会存在较大的性能问题。

//对LitJson进行注册,需要在注册CLR绑定之前
LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appdomain);
//LitJson使用
//将一个对象转换成json字符串
string json = JsonMapper.ToJson(obj);
//json字符串反序列化成对象
JsonTestClass obj = JsonMapper.ToObject<JsonTestClass>(json);


//appdomain.LoadAssembly,他还有一个重载版本,只填入第一个steam,其余会自动补充为null
//第三个空填入new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider()
public void LoadAssembly(Stream stream, Stream symbol, ISymbolReaderProvider symbolReader)
//ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider()
public ISymbolReader GetSymbolReader(ModuleDefinition module, string fileName);
public ISymbolReader GetSymbolReader(ModuleDefinition module, Stream symbolStream)
public object Invoke(string type, string method, object instance, params object[] p)
//方法实现
public object Invoke(string type, string method, object instance, params object[] p)
{
IType type2 = GetType(type);
if (type2 == null)
{
return null;
}
IMethod method2 = type2.GetMethod(method, (p != null) ? p.Length : 0);
if (method2 != null)
{
for (int i = 0; i < method2.ParameterCount; i++)
{
if (p[i] != null && !method2.Parameters[i].TypeForCLR.IsAssignableFrom(p[i].GetType()))
{
throw new ArgumentException("Parameter type mismatch");
}
}
return Invoke(method2, instance, p);
}
return null;
}
//普通调用
appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null);
//HotFix_Project.InstanceClass里的静态方法StaticFunTest
public static void StaticFunTest()
{
UnityEngine.Debug.Log("!!! InstanceClass.StaticFunTest()");
}
//传参调用
appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest2", null, 123);
//HotFix_Project.InstanceClass里的静态方法HotFix_Project.InstanceClass
public static void StaticFunTest2(int a)
{
UnityEngine.Debug.Log("!!! InstanceClass.StaticFunTest2(), a=" + a);
}
//泛型调用
public static void GenericMethod<T>(T a)
{
UnityEngine.Debug.Log("!!! InstanceClass.GenericMethod(), a=" + a);
}
//多值调用ref/out
public void RefOutMethod(int addition, out List<int> lst, ref int val)
{
val = val + addition + id;
lst = new List<int>();
lst.Add(id);
}
Debug.Log("通过IMethod调用方法");
//预先获得IMethod,可以减低每次调用查找方法耗用的时间
IType type = appdomain.LoadedTypes["HotFix_Project.InstanceClass"];
//根据方法名称和参数个数获取方法
IMethod method = type.GetMethod("StaticFunTest2", 1);
appdomain.Invoke(method, null, 123);
void InitializeILRuntime()
{
#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
//由于Unity的Profiler接口只允许在主线程使用,为了避免出异常,需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler
appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
#endif
//这里做一些ILRuntime的注册
//TestDelegateMethod, 这个委托类型为有个参数为int的方法,注册仅需要注册不同的参数搭配即可
appdomain.DelegateManager.RegisterMethodDelegate<int>();
//带返回值的委托的话需要用RegisterFunctionDelegate,返回类型为最后一个
appdomain.DelegateManager.RegisterFunctionDelegate<int, string>();
//Action<string> 的参数为一个string
appdomain.DelegateManager.RegisterMethodDelegate<string>();
//ILRuntime内部是用Action和Func这两个系统内置的委托类型来创建实例的,所以其他的委托类型都需要写转换器
//将Action或者Func转换成目标委托类型
appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateMethod>((action) =>
{
//转换器的目的是把Action或者Func转换成正确的类型,这里则是把Action<int>转换成TestDelegateMethod
return new TestDelegateMethod((a) =>
{
//调用委托实例
((System.Action<int>)action)(a);
});
});
//对于TestDelegateFunction同理,只是是将Func<int, string>转换成TestDelegateFunction
appdomain.DelegateManager.RegisterDelegateConvertor<TestDelegateFunction>((action) =>
{
return new TestDelegateFunction((a) =>
{
return ((System.Func<int, string>)action)(a);
});
});
//下面再举一个这个Demo中没有用到,但是UGUI经常遇到的一个委托,例如UnityAction<float>
appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<float>>((action) =>
{
return new UnityEngine.Events.UnityAction<float>((a) =>
{
((System.Action<float>)action)(a);
});
});
} //补充
//从源码中可以看到以下两种注册都是使用Action和Func进行实现的,整合后会转递给:public void RegisterDelegateConvertor<T>(Func<Delegate, Delegate> action)
//RegisterMethodDelegate最多支持五个泛型
public void RegisterMethodDelegate<T1, T2, T3, T4, T5>()
//RegisterFunctionDelegate最多支持五个泛型,最后一个是返回值
public void RegisterFunctionDelegate<TResult>()
public void RegisterFunctionDelegate<T1, T2, T3, T4, TResult>()
//热更类
public abstract class TestClassBase
{
public virtual int Value
{
get
{
return 0;
}
set
{
}
}
public virtual void TestVirtual(string str)
{
Debug.Log("!! TestClassBase.TestVirtual, str = " + str);
}
public abstract void TestAbstract(int gg);
}
//加载后处理
void OnHotFixLoaded()
{
Debug.Log("首先我们来创建热更里的类实例");
TestClassBase obj;
Debug.Log("现在我们来注册适配器, 该适配器由ILRuntime/Generate Cross Binding Adapter菜单命令自动生成");
appdomain.RegisterCrossBindingAdaptor(new TestClassBaseAdapter());
Debug.Log("现在再来尝试创建一个实例");
//这里的TestInheritance为public class TestInheritance : TestClassBase
//appdomain.Instantiate:public T Instantiate<T>(string type, object[] args = null)
obj = appdomain.Instantiate<TestClassBase>("HotFix_Project.TestInheritance");
Debug.Log("现在来调用成员方法");
obj.TestAbstract(123);
obj.TestVirtual("Hellopublic T Instantiate<T>(string type, object[] args = null)
obj.Value = 233;//public override int Value { get; set; }
Debug.LogFormat("obj.Value={0}", obj.Value);
Debug.Log("现在换个方式创建实例");
obj = appdomain.Invoke("HotFix_Project.TestInheritance", "NewObject", null, null) as TestClassBase;
obj.TestAbstract(456);
obj.TestVirtual("Foobar");
obj.Value = 2333333;
Debug.LogFormat("obj.Value={0}", obj.Value);
}
unsafe void InitializeILRuntime()
{
...
//这里做一些ILRuntime的注册
var mi = typeof(Debug).GetMethod("Log", new System.Type[] { typeof(object) });
//Log_11为重定向方法
appdomain.RegisterCLRMethodRedirection(mi, Log_11);
}
unsafe void OnHotFixLoaded()
{
Debug.Log("请注释和解除InitializeILRuntime方法里的重定向注册,对比下一行日志的变化");
//注册时显示:call System.Void UnityEngine.Debug::Log(System.Object)
/*注释时显示:System.Reflection.MethodBase:Invoke (object,object[])
ILRuntime.CLR.Method.CLRMethod:Invoke*/
appdomain.Invoke("HotFix_Project.TestCLRRedirection", "RunTest", null, null);
}
//CLR绑定,放在void InitializeILRuntime()里的末尾部分
ILRuntime.Runtime.Generated.CLRBindings.Initialize(appdomain);
void Update()
{
if (ilruntimeReady && !executed && Time.realtimeSinceStartup > 3)
{
executed = true;
//这里为了方便看Profiler,代码挪到Update中了
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
var type = appdomain.LoadedTypes["HotFix_Project.TestCLRBinding"];
var m = type.GetMethod("RunTest", 0);
Debug.Log("请解除InitializeILRuntime方法中的注释对比有无CLR绑定对运行耗时和GC开销的影响");
sw.Reset();
sw.Start();
//使用直接调用的方法
Profiler.BeginSample("RunTest");
appdomain.Invoke("HotFix_Project.TestCLRBinding", "RunTest", null, null);
/*使用指定位置后调用的方法
Profiler.BeginSample("RunTest2");
appdomain.Invoke(m, null, null);*/
Profiler.EndSample();
sw.Stop();
Debug.LogFormat("刚刚的方法执行了:{0} ms", sw.ElapsedMilliseconds);
Debug.Log("可以看到运行时间和GC Alloc有大量的差别,RunTest2之所以有20字节的GC Alloc是因为Editor模式ILRuntime会有调试支持,正式发布(关闭Development Build)时这20字节也会随之消失");
}
}
void InitializeILRuntime()
{
...
//使用Couroutine时,C#编译器会自动生成一个实现了IEnumerator,IEnumerator<object>,IDisposable接口的类,因为这是跨域继承,所以需要写CrossBindAdapter(详细请看04_Inheritance教程),Demo已经直接写好,直接注册即可
appdomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
appdomain.DebugService.StartDebugService(56000);//如果要执行单步debug就加上这句话
}
//Demo协程
public class CoroutineDemo : MonoBehaviour
{
static CoroutineDemo instance;
public static CoroutineDemo Instance
{
get {return instance;}
}
}
//HotFix_Project->TestCoroutine代码
public class TestCoroutine
{
public static void RunTest()
{
CoroutineDemo.Instance.DoCoroutine(Coroutine());
}
static System.Collections.IEnumerator Coroutine()
{
Debug.Log("开始协程,t=" + Time.time);
yield return new WaitForSeconds(3);
Debug.Log("等待了3秒,t=" + Time.time);
}
}

void OnHotFixLoaded()
{
Debug.Log("热更DLL中的类型我们均需要通过AppDomain取得");
var it = appdomain.LoadedTypes["HotFix_Project.InstanceClass"];
Debug.Log("LoadedTypes返回的是IType类型,但是我们需要获得对应的System.Type才能继续使用反射接口");
var type = it.ReflectionType;
Debug.Log("取得Type之后就可以按照我们熟悉的方式来反射调用了");
//返回为当前 Type 定义的所有公共构造函数。
var ctor = type.GetConstructor(new System.Type[0]);
var obj = ctor.Invoke(null);
Debug.Log("打印一下结果");
Debug.Log(obj);
Debug.Log("我们试一下用反射给字段赋值");
var fi = type.GetField("id", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
fi.SetValue(obj, 111111);
Debug.Log("我们用反射调用属性检查刚刚的赋值");
var pi = type.GetProperty("ID");
Debug.Log("ID = " + pi.GetValue(obj, null));
}
//注册对应Binder
void InitializeILRuntime()
{
......
//这里做一些ILRuntime的注册,这里我们注册值类型Binder,注释和解注下面的代码来对比性能差别
appdomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
appdomain.RegisterValueTypeBinder(typeof(Quaternion), new QuaternionBinder());
appdomain.RegisterValueTypeBinder(typeof(Vector2), new Vector2Binder());
}
//注册对应Binder
void InitializeILRuntime()
{
......
//这里做一些ILRuntime的注册,这里我们注册值类型Binder,注释和解注下面的代码来对比性能差别
appdomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
appdomain.RegisterValueTypeBinder(typeof(Quaternion), new QuaternionBinder());
appdomain.RegisterValueTypeBinder(typeof(Vector2), new Vector2Binder());
}
| 类型 | 注册耗时 | 不注册耗时 |
|---|---|---|
| Vector3 | 105ms | 2444ms |
| Quaternion | 110ms | 1685ms |
| vector2 | 107ms | 2427ms |

给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我正在尝试为我的iOS应用程序设置cocoapods但是当我执行命令时:sudogemupdate--system我收到错误消息:当前已安装最新版本。中止。当我进入cocoapods的下一步时:sudogeminstallcocoapods我在MacOS10.8.5上遇到错误:ERROR:Errorinstallingcocoapods:cocoapods-trunkrequiresRubyversion>=2.0.0.我在MacOS10.9.4上尝试了同样的操作,但出现错误:ERROR:Couldnotfindavalidgem'cocoapods'(>=0),hereiswhy:U
这太简单了,太荒谬了,我在任何地方都找不到关于它的任何信息,包括API文档和Rails源代码:我有一个:belongs_to关联,我开始理解当您没有关联时您在Controller中调用的正常模型方法与您有关联时调用的方法略有不同。例如,我的关联在创建Controller操作时运行良好:@user=current_user@building=Building.new(params[:building])respond_todo|format|if@user.buildings.create(params[:building])#etcetera但我找不到关于更新如何工作的文档:@user
升级到OSXYosemite后,我现有的pow.cx安装不起作用。升级到最新的pow.cx无效。通过事件监视器重新启动它也没有成功。 最佳答案 卸载(!)并重新安装解决了这个问题。curlget.pow.cx/uninstall.sh|shcurlget.pow.cx|sh 关于ruby-on-rails-OSXYosemite更新破坏了pow.cx,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/q
我们在Ubuntu14.04和Gitlab9.3.7上运行,运行良好。我们正在尝试更新到Gitlabv9.3.8的最新安全补丁,但它给我们这个错误:Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension.currentdirectory:/home/git/gitlab/vendor/bundle/ruby/2.3.0/gems/re2-1.0.0/ext/re2/usr/local/bin/ruby-r./siteconf20170720-19622-15i0edf.rbextconf.rbcheckingformain(
我遇到了以下问题。我有一个名为user的模型,它有一个名为activated的列。我试图通过激活的方法更新该值?但它给我错误:验证失败:密码不能为空,密码太短(最少6个字符)这对我来说没有意义,因为我没有接触密码字段!我只想更新激活的列。我把我认为相关的代码放在这里,但如果你认为你需要更多,请问:)非常感谢您!型号:attr_accessor:passwordattr_accessible:name,:email,:password,:password_confirmation,:activatedhas_many:sucu_votesemail_regex=/\A[\w+\-.]+@
当且仅当模型存在时,我才尝试更新模型的值。如果没有,我什么都不做。搜索似乎只返回更新或创建问题/答案,但我不想创建。我知道我可以用一个简单的方法来做到这一点:found=Model.find_by_id(id)iffoundupdatestuffend但是,我觉得有一种方法可以在一次调用中完成此操作,而无需分配任何临时本地值或执行if。如果记录不存在,我该如何编写一个Rails调用来更新记录而不出现嘈杂错误?最新的Rails3.x 最佳答案 您可以使用try在对find_by_id或where的结果调用update_attribut
我有一个允许更新用户记录的表单。它包含:password和:password_confirmation字段,但我不希望在数据库中已存储加密密码时对它们运行验证。View文件中的字段:'ConfirmPassword'%>在互联网上搜索时,我发现了这段代码,我认为它是针对以前版本的Ruby/Rails的。(我会把它放在我的用户模型中。)validates_presence_of:password,:on=>create由于我的用户模型中密码验证的语法不同(如下),我对我需要的语法感到困惑。validates:password,:presence=>true,:confirmation=>
我是Ruby新手,请多多包涵。我一直在尝试将我的Ruby更新到1.8.7,但在我的MacOSX(10.5.7)上遇到了很多问题。说真的,你如何更新到Ruby1.8.7?谢谢。 最佳答案 最简单的答案是“不要”。Ruby1.8.7是在1.8和1.9之间创建桥接版本的尝试,社区一致认为这没有必要,因此在1.9最终确定之前几乎没有人支持它。一般建议要么坚持使用1.8(该版本随OSX10.5一起提供),要么使用Ruby1.9。MacPorts有适用于Ruby1.8.7和Ruby1.9的软件包,因此这可能是获得更新版本的Ruby的最简单方法,