通过System.Linq.Enumerable在DotPeek中查看,我注意到一些方法是使用[__DynamicallyInvokable]属性调整的.
这个属性扮演什么角色?它是由DotPeek添加的还是它扮演另一个角色,也许告诉编译器如何最好地优化方法?
委托中的Invoke和DynamicInvoke有什么区别?请给我一些代码示例来解释这两种方法之间的区别.
这个问题部分是关于代表,部分是关于泛型.
鉴于简化的代码:
internal sealed class TypeDispatchProcessor
{
private readonly Dictionary<Type, Delegate> _actionByType
= new Dictionary<Type, Delegate>();
public void RegisterProcedure<T>(Action<T> action)
{
_actionByType[typeof(T)] = action;
}
public void ProcessItem(object item)
{
Delegate action;
if (_actionByType.TryGetValue(item.GetType(), out action))
{
// Can this call to DynamicInvoke be avoided?
action.DynamicInvoke(item);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我在其他地方读到,直接调用委托(带括号)比调用快几个数量级DynamicInvoke,这是有道理的.
对于上面的代码示例,我想知道我是否可以执行类型检查并以某种方式提高性能.
一些上下文:我有一个对象流,可以在各种处理程序中运行,这些处理程序可以在运行时注册/取消注册.上面的模式完全符合我的目的,但如果可能的话,我想让它变得更加快捷.
一种选择是存储Action<object>在Dictionary,并Action<T>用另一个委托包装代理.我还没有比较第二次间接调用会影响的性能变化.
给定此类具有隐式转换运算符:
public class MyDateTime
{
public static implicit operator MyDateTime(System.Int64 encoded)
{
return new MyDateTime(encoded);
}
public MyDateTime(System.Int64 encoded)
{
_encoded = encoded;
}
System.Int64 _encoded;
}
Run Code Online (Sandbox Code Playgroud)
我现在可以做以下事情:
long a = 5;
MyDateTime b = a;
Run Code Online (Sandbox Code Playgroud)
但不是以下内容:
long f = 5;
object g = f;
MyDateTime h = g;
Run Code Online (Sandbox Code Playgroud)
这给出了编译时间:
无法将类型'object'隐式转换为'MyDateTime'.
我感觉合理.
现在我修改前面的例子如下:
long f = 5;
object g = f;
MyDateTime h = (MyDateTime)g;
Run Code Online (Sandbox Code Playgroud)
编译好了.现在我得到一个运行时InvalidCastException:
无法将"System.Int64"类型的对象强制转换为"MyDateTime"类型.
这告诉我C#隐式转换运算符仅在编译时应用,并且在.NET运行时试图将对象动态转换为另一种类型时不应用.
我的问题:
顺便说一句,完整的应用程序是我Delegate.DynamicInvoke()用来调用一个带MyDateTime参数的函数,而我传递给的参数的类型很DynamicInvoke长.
c# dynamic-cast type-conversion implicit-cast dynamic-invoke
我在基类中有以下代码:
public static void InvokeExternal(Delegate d, object param, object sender)
{
if (d != null)
{
//Check each invocation target
foreach (Delegate dDelgate in d.GetInvocationList())
{
if (dDelgate.Target != null && dDelgate.Target is System.ComponentModel.ISynchronizeInvoke
&& ((System.ComponentModel.ISynchronizeInvoke)(dDelgate.Target)).InvokeRequired)
{
//If target is ISynchronizeInvoke and Invoke is required, invoke via ISynchronizeInvoke
((System.ComponentModel.ISynchronizeInvoke)(dDelgate.Target)).Invoke(dDelgate, new object[] { sender, param });
}
else
{
//Else invoke dynamically
dDelgate.DynamicInvoke(sender, param);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
此代码示例负责调用一个事件,表示为多播委托,其中调用目标包括不关心跨线程的小类,以及实现ISynchronizeInvoke和关注跨线程的类,如Windows窗体控件.
理论上,这个片段工作得很好,没有错误发生.但是DynamicInvoke速度非常慢,并不是说它是应用程序的当前瓶颈.
所以,我的问题是:有没有办法加快这个小功能而不破坏功能直接订阅事件?
所有活动/代表的签名是 (object sender, EventArgs param)
我有以下代码:
public class MyClass
{
private Delegate m_action;
public object[] m_args;
public MyClass()
{
}
public MyClass(Delegate action, params object[] args)
{
m_args = args;
m_action = action;
}
public void Execute()
{
m_action.DynamicInvoke(m_args);
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法的问题在于 m_args 本身就是一个对象,它的内容没有被扁平化为单独的 params 条目。我怎样才能解决这个问题?
鉴于以下代码行,
Expression<Action> expression = () => target.ToString();
Run Code Online (Sandbox Code Playgroud)
有没有快速获取target对象的方法?
下面的代码有效
public object GetExpressionTarget<T>(Expression<T> expression)
{
MethodCallExpression methodCall = (MethodCallExpression) expression.Body;
LambdaExpression theTarget = Expression.Lambda(methodCall.Object, null);
Delegate compiled = theTarget.Compile();
return compiled.DynamicInvoke(); }
Run Code Online (Sandbox Code Playgroud)
但是非常非常慢。
有没有更快的方法来获取方法调用表达式的目标?
对我的代码 ( GetDelegate,DelegateCompile和DelegateDynamicInvoke) 以及@IvanStoev 的代码 ( GetFunc,FuncCompile和FuncInvoke) 进行基准测试会产生以下结果:
| Method | Mean | Error | StdDev |
|---------------------- |----------------|---------------|---------------|
| DelegateCompile | 165,068.477 ns | 2,671.3001 ns | 2,498.7358 ns |
| FuncCompile …Run Code Online (Sandbox Code Playgroud) c# ×7
delegates ×5
.net ×3
invoke ×2
dynamic-cast ×1
events ×1
expression ×1
generics ×1
parameters ×1
performance ×1