据我所知,有三种方法可以在Ruby中动态调用方法:
方法1:
s = SomeObject.new
method = s.method(:dynamic_method)
method.call
Run Code Online (Sandbox Code Playgroud)
方法2:
s = SomeObject.new
s.send(:dynamic_method)
Run Code Online (Sandbox Code Playgroud)
方法3:
s = SomeObject.new
eval "s.dynamic_method"
Run Code Online (Sandbox Code Playgroud)
通过对它们进行基准测试,我已经确定方法1是最快的,方法2是慢的,方法3是迄今为止最慢的.
我也发现,.call
并且.send
都允许调用私有方法,而eval
不是.
所以我的问题是:有没有理由使用.send
或eval
?为什么你不总是只使用最快的方法?这些调用动态方法的方法有什么其他区别?
我在基于反射的C#代码中遇到了一些动态方法,我还没弄清楚它们究竟是什么.特别是似乎有一个DynamicMethod
类允许在运行时生成和指定CLR方法.但那时还有MethodBuilder
班级.他们似乎都做了非常相似的事情.显然,"动态组件"是AssemblyBuilder
类,"动态类型"是TypeBuilder
类.无论如何,它们都驻留在System.Reflection.Emit
命名空间中.
MSDN似乎在这个主题上有很少的高级信息.因此,如果有人可以解释什么是动态方法,这里的所有XYZBuilder
类都在哪里发挥作用,以及它们各自用于什么,那就太棒了.我应该知道的任何其他Reflection.Emit类型和功能也将受到赞赏.
假设我有以下代码更新struct
使用反射的字段.由于struct实例被复制到DynamicUpdate
方法中,因此需要在传递之前将其装箱到对象.
struct Person
{
public int id;
}
class Test
{
static void Main()
{
object person = RuntimeHelpers.GetObjectValue(new Person());
DynamicUpdate(person);
Console.WriteLine(((Person)person).id); // print 10
}
private static void DynamicUpdate(object o)
{
FieldInfo field = typeof(Person).GetField("id");
field.SetValue(o, 10);
}
}
Run Code Online (Sandbox Code Playgroud)
代码工作正常.现在,假设我不想使用反射,因为它很慢.相反,我想生成一些CIL直接修改id
字段并将该CIL转换为可重用的委托(例如,使用动态方法功能).特别是,我想用s/t替换上面的代码,如下所示:
static void Main()
{
var action = CreateSetIdDelegate(typeof(Person));
object person = RuntimeHelpers.GetObjectValue(new Person());
action(person, 10);
Console.WriteLine(((Person)person).id); // print 10
}
private static Action<object, object> CreateSetIdDelegate(Type t)
{
// build dynamic method …
Run Code Online (Sandbox Code Playgroud) 是否可以将lambda表达式作为IL字节流传递给辅助AppDomain,然后使用DynamicMethod将其组装回那里,以便可以调用它?
我不太确定这是一个正确的方法,所以这是(详细的)原因我问这个问题...
在我的应用程序中,有很多情况需要加载几个程序集进行反射,因此我可以确定接下来要对它们做些什么.问题部分是我需要能够在完成反射之后卸载组件.这意味着我需要使用另一个加载它们AppDomain
.
现在,我的大多数情况都有点类似,但不完全相同.例如,有时我需要返回一个简单的确认,有时我需要从程序集中序列化资源流,有时我还需要进行一两次回调.
因此,我最终AppDomain
一遍又一遍地编写相同的半复杂临时创建代码,并实现自定义MarshalByRefObject
代理以在新域和原始域之间进行通信.
由于这不再是可以接受的,我决定编写一个AssemblyReflector
可以这样使用的类:
using (var reflector = new AssemblyReflector(@"C:\MyAssembly.dll"))
{
bool isMyAssembly = reflector.Execute(assembly =>
{
return assembly.GetType("MyAssembly.MyType") != null;
});
}
Run Code Online (Sandbox Code Playgroud)
AssemblyReflector
将AppDomain
凭借自动卸载IDisposable
,并允许我执行一个透明地Func<Assembly,object>
将反射代码保存在另一个中的-type lambda AppDomain
.
问题是,lambdas不能简单地传递给其他域.所以在搜索之后,我发现看起来像是一种方法:将lambda传递给新AppDomain
的IL作为IL流 - 这让我想到了原始问题.
这是我尝试过的,但没有用(BadImageFormatException
在尝试调用新委托时问题被抛出):
public delegate object AssemblyReflectorDelegate(Assembly reflectedAssembly);
public class AssemblyReflector : IDisposable
{
private AppDomain _domain;
private string _assemblyFile;
public AssemblyReflector(string fileName) { ... }
public void Dispose() { ... …
Run Code Online (Sandbox Code Playgroud) 我试图从动态生成的方法中调用内部方法.il代码很简单:ldarg_0,callvirt,ret.
使用TypeLoadException执行该方法失败,说它无法加载定义内部方法的类型.
当我想到它时,这似乎是合乎逻辑的,因为动态方法主机程序集不是方法声明类型程序集的朋友.
但是,我预计动态方法仍然有效,就像Delegate.CreateDelegate一样.毕竟,我确实设法获得内部方法的MethodInfo,因此权限障碍在我身后.
无论如何,问题是"是否可以从动态生成的方法中调用内部方法?"
谢谢.
编辑:
这是一个简单的代码示例,演示了这个问题:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
namespace A
{
internal class Data
{
internal string String { get; set; }
}
public static class Program
{
public static void Main()
{
Expression<Func<Data, string>> expr = x => x.String;
var getterInfo = ((PropertyInfo)((MemberExpression)expr.Body).Member).GetGetMethod(true);
var getter1 = (Func<Data, string>)Delegate.CreateDelegate(typeof(Func<Data, string>), getterInfo);
var dm = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) });
var gen = dm.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Castclass, typeof(Data));
gen.Emit(OpCodes.Callvirt, getterInfo);
gen.Emit(OpCodes.Ret); …
Run Code Online (Sandbox Code Playgroud) 我很确定Ruby有这些(__ call ,__ get和__set的等价物),因为否则find_by如何在Rails中工作?也许有人可以举例说明如何定义与find_by相同的方法?
谢谢
我们在系统中使用LINQ非常广泛.特别是LINQ到对象.所以在某些地方我们最终会在内存中使用一些巨大的表达式构建LINQ查询.当表达式中存在一些错误时,就会出现问题.所以我们得到NullReferenceException并且堆栈跟踪导致我们无处可去(到[Lightweight Function]).该异常被抛出在LINQ生成的动态方法中.
有没有简单的方法来调试这样的动态方法?或者我是否必须牺牲自己来学习WinDBG?:-)
我正在尝试通过发出动态方法来创建构造函数的委托表示,该方法必须匹配这个非常"松散类型"的签名,以便它可以与任何类型的参数化构造函数一起使用:
public delegate Object ParamsConstructorDelegate(params object[] parameters);
Run Code Online (Sandbox Code Playgroud)
并且创建委托的代码看起来像(注意这是针对Silverlight的)
public static ParamsConstructorDelegate CreateDelegate(ConstructorInfo constructor)
{
Guard.ArgumentNotNull(constructor, "constructor");
Guard.ArgumentValue(constructor.GetParameters().Length == 0, MUSTBE_PARAMETERIZED_CONSTRUCTOR);
var _argumentTypes = new Type[] { typeof(object[]) };
var _parameters = constructor.GetParameters();
var _parameterTypes = _parameters.Select((p) => p.ParameterType).ToArray();
var _sourceType = constructor.DeclaringType;
var _method = new DynamicMethod(constructor.Name, _sourceType, _argumentTypes);
var _gen = _method.GetILGenerator();
for (var _i = 0; _i < _parameters.Length; _i++)
{
if (_parameters[_i].IsOut || _parameterTypes[_i].IsByRef)
{
if (_i < 128)
{
_gen.Emit(OpCodes.Ldarga_S, (byte)_i);
}
else
_gen.Emit(OpCodes.Ldarga, _i);
} …
Run Code Online (Sandbox Code Playgroud) 我希望能够使用Dart类构造函数执行此类操作:
class Model {
// ... setting instance variables
Model(Map fields) {
fields.forEach((k,v) => this[k] = v);
}
}
Run Code Online (Sandbox Code Playgroud)
显然,这不起作用,因为this
没有[]=
方法.
有没有办法让它发挥作用,或者它根本不是做事的"飞镖方式"?如果不是,你能告诉我解决这个问题的正确方法吗?
class A { void F() { System.out.println("a"); }}
class B extends A { void F() { System.out.println("b"); }}
public class X {
public static void main(String[] args) {
A objA = new B();
objA.F();
}
}
Run Code Online (Sandbox Code Playgroud)
这里F()
是动态调用的,不是吗?
这篇文章说:
... Java字节码不支持动态方法调用.有三种受支持的调用模式:invokestatic,invokespecial,invokeinterface或invokevirtual.这些模式允许调用具有已知签名的方法.我们谈论强类型语言.这允许在编译时直接进行一些检查.
另一方面,动态语言使用动态类型.所以我们可以在编译时调用一个未知的方法,但是使用Java字节码完全不可能.
我错过了什么?
dynamic-method ×10
.net ×4
c# ×4
reflection ×2
ruby ×2
appdomain ×1
bytecode ×1
cil ×1
clr ×1
constructor ×1
dart ×1
dart-mirrors ×1
debugging ×1
delegates ×1
dynamic ×1
java ×1
lambda ×1
linq ×1