繁体   English   中英

在C#中消除访客模式

[英]Eliminating visitor pattern in C#

我有看起来像这样的代码:

var visitor = new ImplementsVisitor();
for(int i = 0; i < array.Length; ++i)
    arrray[i].Accept(visitor);

数组中的每个元素都实现IItem接口,该接口具有Accept(IVisitor)方法。 只是标准的访客模式。

在评估性能时,我得出的结论是,接口调用太慢,并且在此代码中,性能至关重要。 根据您的经验,消除任何虚拟或接口调用的最佳选择是什么? 是否检查具体类型的if语句? 每个带有开关/大小写的元素上的枚举(在这种情况下,代码的结构使得不需要强制转换)? 还有吗

PS我不能排序数组中的项目。 顺序很重要。 因此,我无法按具体类型对它们进行排序以帮助进行分支预测。

我创建了以下程序。 在我的笔记本电脑上,循环在8ms内运行了100万次(这是Release版本,Debug在11ms左右)。 进行虚拟调度并增加一个int大约需要0.000008ms。 您到底需要多快? 我怀疑您的性能测试或我的问题出了问题。 如果是我的,我将有兴趣听取改进建议。

通常,如果此级别的性能不够好,那么使用C#本身可能就是一个问题。 例如,其垃圾收集器习惯于在循环中间冻结线程。 如果在循环迭代上确实使用0.000008ms是个问题,我怀疑汇编语言或C是更好的选择。

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            const int count = 1000000;

            IList<IItem> items = new List<IItem>(count);
            for (int i = 0; i < count; i++)
            {
                var rnd = new Random();
                if (rnd.NextDouble() > 0.5)
                {
                    items.Add(new ClassA());
                }
                else
                {
                    items.Add(new ClassB());
                }
            }

            var visitor = new MyVisitor();

            Stopwatch s = Stopwatch.StartNew();
            for (int i = 0; i < items.Count; i++)
            {
                items[i].Accept(visitor);
            }
            s.Stop();

            Console.WriteLine("ExecTime = {0}, Per Cycle = {1}", s.ElapsedMilliseconds, (double)s.ElapsedMilliseconds / count);
            visitor.Output();
        }

        interface IVisitor
        {
            void Process(ClassA item);
            void Process(ClassB item);
        }

        interface IItem
        {
            void Accept(IVisitor visitor);
        }

        abstract class BaseVisitor : IVisitor
        {
            public virtual void Process(ClassA item)
            {

            }

            public virtual void Process(ClassB item)
            {

            }
        }

        class ClassA : IItem
        {
            public void Accept(IVisitor visitor)
            {
                visitor.Process(this);
            }
        }

        class ClassB : IItem
        {
            public void Accept(IVisitor visitor)
            {
                visitor.Process(this);
            }
        }

        class MyVisitor : BaseVisitor
        {
            int a = 0;
            int b = 0;

            public override void Process(ClassA item)
            {
                a++;
            }

            public override void Process(ClassB item)
            {
                b++;
            }

            public void Output()
            {
                Console.WriteLine("a = {0}, b = {1}", a, b);
            }
        }
    }
}

您在这里没有一个虚拟呼叫,只有两个,但是您只需要一个。 首先,您的数组可能通过IItem进行了虚拟调用-但是,如果这些都是相同的类型,并且您知道该类型(并且是密封的),则不需要进行虚拟调用。

然后,在访问的对象内,您需要执行访问者想要执行的任何操作。 这可能还会涉及虚拟呼叫。

使用类型化的IVisitor可能会做得更好:

 interface IItem<TVisitor> : IItem 
     where TVisitor : IVisitor
 {
     void Accept(TVisitor visitor);
 }

 // Then
 SpecialVisitor visitor = ImplementsSpecialVisitor();
 foreach(var item in arrayOfSpecialItems){
     item.Accept<SpecialVisitor>(visitor);
 }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM