Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/flash/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在这个通用代码中可以避免Delegate.DynamicInvoke吗?_C#_.net_Generics_Delegates_Dynamic Invoke - Fatal编程技术网

C# 在这个通用代码中可以避免Delegate.DynamicInvoke吗?

C# 在这个通用代码中可以避免Delegate.DynamicInvoke吗?,c#,.net,generics,delegates,dynamic-invoke,C#,.net,Generics,Delegates,Dynamic Invoke,这个问题部分是关于代理的,部分是关于泛型的 根据简化代码: internal sealed class TypeDispatchProcessor { private readonly Dictionary<Type, Delegate> _actionByType = new Dictionary<Type, Delegate>(); public void RegisterProcedure<T>(Action<T

这个问题部分是关于代理的,部分是关于泛型的

根据简化代码:

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);
        }
    }
}
内部密封类类型DispatchProcessor
{
专用只读字典_actionByType
=新字典();
公共无效登记程序(行动)
{
_actionByType[类型(T)]=动作;
}
public void ProcessItem(对象项)
{
授权行动;
if(_actionByType.TryGetValue(item.GetType(),out action))
{
//可以避免调用DynamicVoke吗?
行动.动力Voke(项目);
}
}
}
我读到直接调用委托(带括号)比调用
DynamicInvoke
快几个数量级,这是有意义的

对于上面的代码示例,我想知道是否可以执行类型检查并以某种方式提高性能

一些上下文:我有一个对象流,这些对象被分配给不同的处理程序,这些处理程序可以在运行时注册/注销。对于我来说,上面的模式功能完美,但如果可能的话,我想让它更简洁


一个选项是将
操作
存储在
字典
中,并用另一个代理包装
操作
代理。我还没有比较第二个间接调用将影响的性能变化。

我强烈怀疑包装调用将比使用
DynamicInvoke
更有效。您的代码将是:

internal sealed class TypeDispatchProcessor
{
    private readonly Dictionary<Type, Action<object>> _actionByType 
        = new Dictionary<Type, Action<object>>();

    public void RegisterProcedure<T>(Action<T> action)
    {
        _actionByType[typeof(T)] = item => action((T) item);
    }

    public void ProcessItem(object item)
    {
        Action<object> action;
        if (_actionByType.TryGetValue(item.GetType(), out action))
        {
            action(item);
        }
    }
}
内部密封类类型DispatchProcessor
{
专用只读字典_actionByType
=新字典();
公共无效登记程序(行动)
{
_actionByType[typeof(T)]=项目=>行动((T)项目);
}
public void ProcessItem(对象项)
{
行动;
if(_actionByType.TryGetValue(item.GetType(),out action))
{
行动(项目);
}
}
}

这是值得的基准测试,但我认为你会发现这更有效
DynamicInvoke
必须使用反射等来检查所有参数,而不是在包装的委托中进行简单的强制转换。

因此我对此进行了一些测量

var delegates = new List<Delegate>();
var actions = new List<Action<object>>();

const int dataCount = 100;
const int loopCount = 10000;

for (int i = 0; i < dataCount; i++)
{
    Action<int> a = d => { };
    delegates.Add(a);
    actions.Add(o => a((int)o));
}

var sw = Stopwatch.StartNew();
for (int i = 0; i < loopCount; i++)
{
    foreach (var action in actions)
        action(i);
}
Console.Out.WriteLine("{0:#,##0} Action<object> calls in {1:#,##0.###} ms",
    loopCount * dataCount, sw.Elapsed.TotalMilliseconds);

sw = Stopwatch.StartNew();
for (int i = 0; i < loopCount; i++)
{
    foreach (var del in delegates)
        del.DynamicInvoke(i);
}
Console.Out.WriteLine("{0:#,##0} DynamicInvoke calls in {1:#,##0.###} ms",
    loopCount * dataCount, sw.Elapsed.TotalMilliseconds);
var delegates=新列表();
var actions=新列表();
常数int数据计数=100;
const int loopCount=10000;
对于(int i=0;i{};
增加(a);
添加(o=>a((int)o));
}
var sw=Stopwatch.StartNew();
for(int i=0;i
我创建了一些间接调用的项,以避免JIT可能执行的任何类型的优化

结果非常令人信服

1,000,000 Action calls in 47.172 ms 1,000,000 Delegate.DynamicInvoke calls in 12,035.943 ms 1,000,000 Action calls in 44.686 ms 1,000,000 Delegate.DynamicInvoke calls in 12,318.846 ms 47.172毫秒内完成1000000次行动呼叫 1000000个Delegate.DynamicVoke呼叫12035.943毫秒 44.686毫秒内完成1000000次行动呼叫 1000000个Delegate.DynamicInvoke呼叫12318.846毫秒
因此,在这种情况下,用对
DynamicInvoke
的调用替换额外的间接调用和cast大约快了270倍。如果您需要将此扩展到包装来自类的成员调用而不使用反射,则可以通过创建一系列编译器提示来实现。编译器提示可以映射类和函数参数列表或返回类型

基本上,您需要创建以对象为参数并返回对象的lambda。然后使用编译器看到的AOT的通用函数来创建适当方法的缓存,以调用成员并强制转换参数。诀窍是创建打开的委托并将其传递给第二个lamda,以便在运行时获得底层提示

您必须为每个类和签名(但不是每个方法或属性)提供提示

我设计了一个这样的类,在这篇文章中列出有点太长了


在性能测试中,它没有上面的例子那么好,但它是通用的,这意味着它在我需要的环境中工作。与调用相比,读取属性的性能大约是调用的4.5倍。

但是,这可能会导致奇怪、混乱的stacktrace。我怀疑它是否会比调用DynamicInvoke产生的更混乱。@Jon,感谢您为我的怀疑增加了一些分量。明天在办公室时我会尝试分析这个问题。很好的解决方案,经过测试,性能大约是10倍better@ValBakhtin,我粗略地测量了一下。那是几年前的事了——CLR可能已经改变了。