声明/传递结构与声明/传递单个值

时间:2010-02-19 16:31:36

标签: c# .net constructor struct performance

实际上我有两个问题。但他们关系密切。

假设我有一个像这样的简单struct

public struct TradePrice {
    public float Price;
    public int Quantity;

    public TradePrice(float price, int quantity) {
        Price = price;
        Quantity = quantity;
    }
}

问题1

现在,如果我的代码需要访问这些值,那么执行此操作之间是否存在任何差异:

TradePrice tp = new TradePrice(50.0f, 100);

而且:

float p = 50.0f;
int q = 100;

我觉得第一个,因为它调用一个构造函数,应该有类似方法调用的开销。但这只是猜测。

问题2

上述问题的答案实际上也可能回答了这个问题,但也许有一些微妙之处使得这些情景不同;所以无论如何我会问。假设我有一个struct,它本身的成员是struct类型。像这样:

public struct BidAsk {
    public TradePrice Bid;
    public TradePrice Ask;
}

那么这三种方法之间会有什么区别吗?

Reset(TradePrice bid, TradePrice ask) {
    Bid = bid;
    Ask = ask;
}

Reset(float bidPrice, int bidQuantity, float askPrice, int askQuantity) {
    Bid = new TradePrice(bidPrice, bidQuantity);
    Ask = new TradePrice(askPrice, askQuantity);
}

Reset(float bidPrice, int bidQuantity, float askPrice, int askQuantity) {
    Bid.Price = bidPrice;
    Bid.Quantity = bidQuantity;
    Ask.Price = askPrice;
    Ask.Quantity = askQuantity;
}

倾向认为struct的构造函数存在某些开销,因此即使传递了相同数量的数据/在上面的方法中设置,第一个和最后一个可能略微更有效。这是真的?第一种也是最后一种方法甚至不同?

为了记录,我不是在问这个问题,因为我错误地认为这会导致性能瓶颈,或者从设计的角度来看尤其重要(尽管也许有人会因为争论我而感到惊讶它 )。我问,因为我只是好奇。

3 个答案:

答案 0 :(得分:5)

首先要解决你的问题。这段代码闻起来很糟糕。

问题#1:您使用二进制浮点数来表示财务数量。二进制浮点数用于表示科学数量,其中精度的最后几位中的微小误差不重要。它们非常不适合代表确切的金融数量。 如果您要表示财务数量,请始终使用小数,请勿使用浮点数。

问题#2:假设你有充分的理由使用二进制浮点数,请不要使用float。使用双。现代硬件经过优化,可以进行双精度算术,而不是单精度;在某些情况下,单精度实际上较慢。当然,double占用的内存是float的两倍,但除非你在内存中同时拥有几百万这些内存,否则它无关紧要。此外,你应该使用十进制,这比浮动大四倍。

问题#3:你有一个可变的值类型。可变值类型是纯粹的邪恶。使用可变值类型意外地导致错误很容易。避免避免。

问题#4:这真的是结构的正确名称吗?我认为“交易价格”将由Price属性表示。是否有一个更好的名称,更清楚地描述它代表什么?它是否代表了您的业务领域中的一些明确概念?

问题#5:对传入公共结构的公共构造函数的参数没有验证。这似乎很危险。如果有人试图以每股-100000.00的价格交易-100股,该怎么办?怎么了?没什么好的,我敢打赌。

问题#6:struct的默认值应该始终是struct的有效实例。是吗?这个结构的默认实例是Price = 0,Quantity = 0.这实际上有意义吗?如果没有,那么不要将其作为结构。把它变成一个类。

问题#7:这首先是逻辑上的值类型吗?为什么它是一个结构?这种东西你觉得应该在逻辑上被视为一个价值,比如12号,或者逻辑上你可以在多个地方引用“同一件事”,比如顾客?如果它在逻辑上不是值,请不要使用值类型。

假设名称正确并且这在逻辑上是值类型,则代码应该类似于:

public struct TradePrice 
{ 
    public decimal Price {get; private set;}
    public int Quantity {get; private set; }
    public TradePrice(decimal price, int quantity) : this()
    { 
        if (price < 0m) throw new ...
        if (quantity < 0) throw new ...
        this.Price = price; 
        this.Quantity = quantity; 
    } 
} 

至于你的实际问题:没有逻辑的区别。 可能是性能差异。我对这里是否存在可衡量的性能差异一无所知。你已经用两种方式写了代码。拿一个秒表,双向运行十亿次,然后你就会知道问题的答案。

答案 1 :(得分:4)

传递结构而不是单个值可能会带来更好的可维护性,因为如果您需要更改结构定义,则代码更改的影响会更低。

答案 2 :(得分:0)

一个结构,例如三个字段的行为很像一组三个变量与胶带粘在一起。分配结构类型的字段或变量为这三个单独的字段分配空间,并且按值传递结构类型的值与分别传递三个字段的运行时成本基本相同。

结构与一组单个变量之间的主要区别是:(1)结构类型的单个数组包含每个元素中的所有结构域。因此,如果MyPoint3d是结构,则一个包含50个元素的数组将包含xyz的所有50个值。如果一个人使用不同坐标的单独字段,则必须创建三个不同的数组; (2)传递任何大小的结构作为ref参数只需要传递一个引用。接受结构类型ref参数的代码(例如MyPoint3d)将知道通过该引用访问的xyz成员都属于到同一个struct实例。相比之下,如果代码接受了x坐标的ref参数,y的另一个参数和z的另一个参数,则无法知道它们实际上是x y单点的1}},zthis坐标。

顺便说一句,结构体暴露公共字段(如果一个结构体应该代表一组变量,没有更好的方法可以显示),但结构方法修改ref是不好的。最好使用接受结构类型的ref参数的静态方法。如果尝试将只读结构作为声明的this参数传递,编译器将发出尖叫声;相反,如果尝试将只读结构传递给修改ref的方法,则不会发出尖叫声。生成的代码将无法正常工作,但它不会生成任何编译器诊断。使用显式{{1}}参数将确保无法编译的代码将无法编译。