Reasignación de variables miembro de una estructura a través de propiedades

So I have a struct that is defined in the following manner:

    public struct Item
{
    public string _name { get; set; }
    public double _weight
    {
        get 
        {
            return _weight;
        }
        set
        {
            _weight = value;

            //Shipping cost is 100% dependent on weight. Recalculate it now.
            _shippingCost = 3.25m * (decimal)_weight;

            //Retail price is partially dependent on shipping cost and thus on weight as well.  Make sure retail price stays up to date.
            _retailPrice = 1.7m * _wholesalePrice * _shippingCost;
        }
     }
    public decimal _wholesalePrice
    {
        get
        {
            return _wholesalePrice;
        }
        set
        {
            //Retail price is partially determined by wholesale price.  Make sure  retail price stays up to date.
            _retailPrice = 1.7m * _wholesalePrice * _shippingCost;
        }
    }
    public int _quantity { get; set; }
    public decimal _shippingCost { get; private set; }
    public decimal _retailPrice { get; private set; }


    public Item(string name, double weight, decimal wholesalePrice, int quantity) : this()
    {
        _name = name;
        _weight = weight;
        _wholesalePrice = wholesalePrice;
        _quantity = quantity;
    }
//More stuff

I also have an instance of Item in a different class. When I try to invoke the weight property through the following command, the program crashes:

currentUIItem._weight = formattedWeight;

A descriptive error is not provided. Note that at this point, currentUIItem has been newed with the parameterless default constructor. Now, here is the weird part. When I remove the custom implementation of weight's set property and replace it with a generic { get; set; }, the assignment works flawlessly.

Does anyone know what's going on here? Is this a quirk of structs that would work fine with classes?

preguntado el 29 de julio de 12 a las 21:07

Structs should be immutable : stackoverflow.com/a/3753640/588868 -

Everything about this says it should be a classno es un struct. It won't impact the infinite recursion, but: worth noting. -

Why do you say that this should definitely be a class? I read that structs debemos be immutable, but since I am only working with a single thread, I do not see how I am going to inadvertently experience problems. -

I disagree with the notion that it should be a class, but I don't like the design of the struct. Whenever practical, structs should be designed so that their state can be contained entirely in public fields which will behave sensibly, and should have no members, other than constructors, that modify this. I would eliminate the _ShippingCost y _RetailPrice fields, but instead have them as read-only properties which are computed based on Weight y WholesalePrice unless you want to allow the retail price to be explicitly set to 'override' that computation. In that case... -

...I would suggest perhaps having a RetailPriceOverride field with defined semantics, or perhaps using an immutable struct or class. I would generally recommend against using a mutable class, since their semantics can be murky unless they're used in a way very different from mutable ones. -

4 Respuestas

You have recursive call to _weight propiedad en tu _weight propiedad set método.

Respondido 29 Jul 12, 21:07

It looks like this would cause a StackOverflowException because you have an infinite recursion.

If you put a breakpoint in your setter:

public double _weight
{
    set
    {
        _weight = value;
    }
 }

You will see that the breakpoint continues to get hit. This is because the setter tries to set a value to _weight. But _weight is not a variable... so when you try to set its value, you are only calling back into the setter method. This continues to happen infinitely. The same applies to the _wholesalePrice property... you probably want something more like this:

public struct Item
{
    public string _name { get; set; }

    private double _weightInternal;
    public double _weight
    {
        get 
        {
            return _weightInternal;
        }
        set
        {
            _weightInternal = value;

            //Shipping cost is 100% dependent on weight. Recalculate it now.
            _shippingCost = 3.25m * (decimal)_weightInternal;

            //Retail price is partially dependent on shipping cost and thus on weight as well.  Make sure retail price stays up to date.
            _retailPrice = 1.7m * _wholesalePriceInternal * _shippingCost;
        }
    }

    private decimal _wholesalePriceInternal;
    public decimal _wholesalePrice
    {
        get
        {
            return _wholesalePriceInternal;
        }
        set
        {
            //Retail price is partially determined by wholesale price.  Make sure  retail price stays up to date.
            _wholesalePriceInternal = value;
            _retailPrice = 1.7m * _wholesalePriceInternal * _shippingCost;
        }
    }
    public int _quantity { get; set; }
    public decimal _shippingCost { get; private set; }
    public decimal _retailPrice { get; private set; }


    public Item(string name, double weight, decimal wholesalePrice, int quantity) : this()
    {
        _name = name;
        _weightInternal = weight;
        _wholesalePriceInternal = wholesalePrice;
        _quantity = quantity;
    }
    //More stuff
}

Respondido 29 Jul 12, 21:07

Usually StackOverflowException, to be specific - marc gravell

I didn't realize that the property was invoked every single time the weight variable was referenced. That's good to know! Thank you! - Nick

No problem... glad it helped out. - glen hughes

The Setter of _weight property is calling its self recursively and causing StackOverFlow exception

The easiest way to fix this will be have a backing field for the Property this way

private double  _weight;
public double Weight
    {
        get 
        {
            return _weight;
        }
        set
        {
            _weight = value;

            //Shipping cost is 100% dependent on weight. Recalculate it now.
            _shippingCost = 3.25m * (decimal)_weight;

            //Retail price is partially dependent on shipping cost and thus on weight as well.  Make sure retail price stays up to date.
            _retailPrice = 1.7m * _wholesalePrice * _shippingCost;
        }
     }

Respondido 29 Jul 12, 21:07

In the setter for _weight, you are trying to set _weight, so this results in an infinite recursion.

Try something like (not including the additional logic you have for retail and shipping costs):

private double _weight;
public double Weight
{
     get { return _weight; }
     set { _weight = value; }
}

Usando solo get; set; tells the compiler to generate a backing field automatically for you and has the same effect as the above code.

Note also the naming convention used, fields (_weight) are prefixed with an underscore and should always be private. The outside world interacts with fields using properties (Weight). This pattern is in place all throughout the .NET framework, see ¿Cuál es la diferencia entre un campo y una propiedad en C #? para obtener más información.

contestado el 23 de mayo de 17 a las 12:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.