界面强制转换与类强制转换

Squ*_*ama 17 .net c# casting interface

我被引导相信在某些情况下,铸造可能成为可衡量的性能障碍.当我们开始处理令人讨厌的异常投掷\捕获的不连贯网络时,情况可能会更多.

鉴于我希望在编程时创建更正确的启发式方法,我已经被提示向.NET专家提出这个问题:接口是否比类型转换更快?

为了给出一个代码示例,让我们说这存在:

public interface IEntity { IParent DaddyMommy { get; } }
public interface IParent : IEntity { }
public class Parent : Entity, IParent { }
public class Entity : IEntity
{
    public IParent DaddyMommy { get; protected set; }
    public IParent AdamEve_Interfaces
    {
        get
        {
            IEntity e = this;
            while (e.DaddyMommy != null)
                e = e.DaddyMommy as IEntity;
            return e as IParent;
        }   
    }
    public Parent AdamEve_Classes
    {
        get
        {
            Entity e = this;
            while (e.DaddyMommy != null)
                e = e.DaddyMommy as Entity;
            return e as Parent;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

那么,AdamEve_Interfaces比AdamEve_Classes快吗?如果是这样,多少钱?而且,如果你知道答案,为什么?

Eri*_*ert 17

这里的一些答案建议基准测试,这是朝着正确方向迈出的一步,但这只是旅程的第一步.

我的团队在这方面做了大量的分析和基准测试.简短版本是肯定的,在某些情况下,接口会产生较小但可衡量的性能成本.然而,实际成本取决于很多因素,包括支持多少接口,给定引用的接口数量,访问模式等等.CLR具有很多启发式功能,旨在加快常见情况下的接口访问速度.

如果您正在对其中一个常见案例进行基准测试,但您的实际程序属于不太常见的情况,那么您的基准测试是有害的,因为它会为您提供误导性数据.

真实代码上进行真实的性能测量要好得多.使用分析器,双向编写代码,并以可见且与用户相关的方式查看这两种方式是否可测量,重复性更快.

至于你对投掷和捕捉的提及:投掷和捕捉的表现成本应该是无关紧要的.根据定义,例外是例外的,不常见.此外,例外通常表明某些事情很快就会停止; 通常情况下,某些事情是否会尽快停止并不重要.如果您遇到异常门控的情况,那么您需要解决更大的问题:停止抛出这么多异常.抛出的异常应该非常罕见.


rse*_*nna 7

看看这里:

http://thatstoday.com/robbanp/blog/6/25/csharp-performance--cast-vs-interface

而且,是的,你似乎是对的.

编辑好吧,似乎我错了.就像我的"patrício"Martinho Fernandes评论一样,上面的链接完全是假的(但为了诚实的编辑,我会把它保留在这里).

我现在有空闲时间,所以我写了一个简单的性能测量代码:

public partial class Form1 : Form
{
    private const int Cycles = 10000000;

    public interface IMyInterface
    {
        int SameProperty { get; set; }
    }

    public class InterfacedClass : IMyInterface
    {
        public int SameProperty { get; set; }
    }

    public class SimpleClass
    {
        public int SameProperty { get; set; }
    }

    public struct InterfacedStruct : IMyInterface
    {
        public int SameProperty { get; set; }
    }

    public struct SimpleStruct
    {
        public int SameProperty { get; set; }
    }

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e) {
        var simpleClassTime = MeasureSimpleClass();
        var interfacedClassTime = MeasureInterfacedClass();
        var simpleStructTime = MeasureSimpleStruct();
        var interfacedStructTime = MeasureInterfacedStruct();

        var message = string.Format(
            "simpleClassTime = {0}\r\ninterfacedClassTime = {1}\r\nsimpleStructTime = {2}\r\ninterfacedStructTime = {3}",
            simpleClassTime,
            interfacedClassTime,
            simpleStructTime,
            interfacedStructTime
        );

        textBox.Text = message;
    }

    private static long MeasureSimpleClass() {
        var watch = Stopwatch.StartNew();
        var obj = new SimpleClass();

        for (var i = 0; i < Cycles; i++)
        {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }

    private static long MeasureInterfacedClass() {
        var watch = Stopwatch.StartNew();
        IMyInterface obj = new InterfacedClass();

        for (var i = 0; i < Cycles; i++) {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }

    private static long MeasureSimpleStruct()
    {
        var watch = Stopwatch.StartNew();
        var obj = new SimpleStruct();

        for (var i = 0; i < Cycles; i++)
        {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }

    private static long MeasureInterfacedStruct()
    {
        var watch = Stopwatch.StartNew();
        IMyInterface obj = new InterfacedStruct();

        for (var i = 0; i < Cycles; i++)
        {
            obj.SameProperty = i;
            var j = obj.SameProperty;
            obj.SameProperty = j;
        }

        return watch.ElapsedMilliseconds;
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

simpleClassTime = 274
interfacedClassTime = 339
simpleStructTime = 247
interfacedStructTime = 302
Run Code Online (Sandbox Code Playgroud)

我真的曾经认为接口对于class类型会更快,而且速度更慢struct(因为后者涉及装箱/拆箱),但事实并非如此:具体的类/结构参考似乎总是更快.

此外,它可能涉及到谁:我认为性能不是决定是否应该使用接口的良好标准.与其他人所说的不同的是,差别可以忽略不计.

  • 我发现"你错了"没有解释是粗鲁的.这就像一个没有解释的downvote. (6认同)
  • @Martinho Fernandes:嗯,你们一点都粗鲁; 你花了一点时间才到达那里.但是,请看看我编辑的答案.你是对的,我错了,所以*mea maxima culpa*. (2认同)
  • 为“可能关注的人”建议+1。 (2认同)

Hen*_*man 7

你必须衡量.

但是如果你的代码成为一个(潜在的)瓶颈,你就会在问题菜单上超越意大利面.


Chr*_*aas 5

您是否尝试过测试?这是一个运行10,000,000次的循环。在我的机器上,接口版本大约需要440毫秒,而类版本大约需要410毫秒。如此接近,但总体而言,该类版本获胜。

using System;

namespace ConsoleApplication1
{
    public interface IEntity { IParent DaddyMommy { get; } }
    public interface IParent : IEntity { }
    public class Parent : Entity, IParent { }
    public class Entity : IEntity
    {
        public IParent DaddyMommy { get; protected set; }
        public IParent AdamEve_Interfaces
        {
            get
            {
                IEntity e = this;
                while (this.DaddyMommy != null)
                    e = e.DaddyMommy as IEntity;
                return e as IParent;
            }
        }
        public Parent AdamEve_Classes
        {
            get
            {
                Entity e = this;
                while (this.DaddyMommy != null)
                    e = e.DaddyMommy as Entity;
                return e as Parent;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Entity X = new Entity();
            Parent P;
            IParent IP;
            System.Diagnostics.Stopwatch ST = new System.Diagnostics.Stopwatch();
            Int32 i;

            ST.Start();
            for (i = 0; i < 10000000; i++)
            {
                IP = X.AdamEve_Interfaces;
            }
            ST.Stop();
            System.Diagnostics.Trace.WriteLine(ST.ElapsedMilliseconds);

            ST.Reset();

            ST.Start();
            for (i = 0; i < 10000000; i++)
            {
                P = X.AdamEve_Classes;
            }
            ST.Stop();
            System.Diagnostics.Trace.WriteLine(ST.ElapsedMilliseconds);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 您可能表示** class **版本胜出(速度更快)。 (2认同)