-2
public class Price
{
  public string Symbol {get; set; }
  public double AskPrice{get; set; }
  public double BidPrice{get; set; }
  public string Exchange{get; set; }
}

public class inputs 
{
  public IList<Price> Prices {get; set; }
}

var inputs = new 
{
Prices = prices,
};

Price[] p = inputs.Prices.Where(x => x.Exchange == exchange).ToArray();

p.ForEach(x => x.AskPrice = 0);

For this code when I create new variable p, it is actually a reference to input.price, not a new variable. Why is this? Is there any best practice of how to deal with this behavior?

JOHN
  • 871
  • 1
  • 12
  • 24
  • 2
    I'm not sure what your question is. `p` isn't `inputs.Prices`, it's a new variable containing a new array, which contains the same `Price` objects that were in `inputs`. Is that not what you expect to happen? – Avner Shahar-Kashtan Jul 11 '16 at 18:36
  • No. p is actually partial input.Prices. The last line, I made change to p. This change will actually reflected in input.Prices. This input.Price will have AskPrice = 0. – JOHN Jul 11 '16 at 18:38
  • `p` is indeed a new variable which been created from prices. clarify our question a bit more. – Rahul Jul 11 '16 at 18:38
  • 1
    You did not make a change to `p`, `p` stayed the same, what you changed where ***the elements inside of `p`***, the elements inside of `p` are shared between `p` and the original source. – Scott Chamberlain Jul 11 '16 at 18:39
  • This is how reference types work in c# – Ehsan Sajjad Jul 11 '16 at 18:39
  • @JOHN No, `p` is a completely different memory space from `inputs.Prices`. However, `p` and `inputs.Prices` contain references to the same objects, so when you update one of those objects using either reference, you'll see the effect from the other. – itsme86 Jul 11 '16 at 18:40
  • @ScottChamberlain Is this useful in any sense? But if you do something like `a = 0; b=a; b = 3; writeline(a)` it won't change. – JOHN Jul 11 '16 at 18:41
  • @JOHN Think of your `Price` objects like a painting in a museum. MuseumPatronA is looking at the painting and so is MuseumPatronB. If MuseumPatronA alters the painting, MuseumPatronB sees the change. That doesn't mean the two patrons are the same, it means they're looking at the same painting. The only way to change that behavior is to make them look at two separate paintings (e.g. by copying the painting). – itsme86 Jul 11 '16 at 18:44
  • @JOHN yes but `a.Foo = 1; itemsA[0] = a; itemB[0] = a; itemsA[0].Foo = 2`, do you expect `a.Foo` and `ItemsB[0]` to not change in that situation? All 3 still point to the same object in memory, your above code is pretty much doing that example. – Scott Chamberlain Jul 11 '16 at 18:45
  • @ScottChamberlain Your example make sense. Thanks. – JOHN Jul 11 '16 at 18:49
  • If your prices are monetary amounts you should be using `decimal`, not `double`. – Dour High Arch Jul 11 '16 at 18:50
  • Possible duplicate of [Reference type in C#](http://stackoverflow.com/questions/18229463/reference-type-in-c-sharp) – JOHN Feb 07 '17 at 22:09

2 Answers2

2

You did not make a change to p, p stayed the same, what you changed where the elements inside of p, the elements inside of p are shared between p and the original source.

To not get this behavior you need to "Deep copy" the objects when you make a new array, creating new objects for the elements with the same content as the original.

public class Price
{
  public string Symbol {get; set; }
  public double AskPrice{get; set; }
  public double BidPrice{get; set; }
  public string Exchange{get; set; }

  public Price Clone()
  {
    var result = new Price();
    result.Symbol = this.Symbol;
    result.AskPrice = this.AskPrice;
    result.BidPrice = this.BidPrice;
    result.Exchange = this.Exchange;
    return result;
  }
}

public class inputs 
{
  public IList<Price> Prices {get; set; }
}

var inputs = new 
{
Prices = prices,
};

Price[] p = inputs.Prices.Where(x => x.Exchange == exchange).Select(x=> x.Clone()).ToArray();

p.ForEach(x => x.AskPrice = 0);

Note, if you have any reference types inside of your class you need to recursively clone the entire data structure and will need to make copies of them too.

Community
  • 1
  • 1
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
1

There are two different variables here - the first is the Price object(s), and the second is input.Prices, which is a List of prices.

Your LINQ code takes the inputs.Prices list, filters it and creates a new array from it, but all that does is create new collections. It doesn't change the actual objects that are in the collection. This is because classes, in C#, are all reference types, meaning that var price = input.Prices[0] just copies the reference to a single, specific instance in memory. You can copy those references between a dozen lists and arrays, but the objects are the same.

It seems that what you want is to clone or copy by value your Price objects. For that, you have two options:

  1. Make Price a struct.

Structs, unlike classes, are value types and are copied-by-value, meaning a new copy is made whenever you assign it to a new variable. This, however, has a performance penalty, since the whole struct is copied every time it's assigned. Your struct takes up 24-32 bytes (two 64bit doubles and two 32/64 bit references to strings), which is more than the recommended rule of thumb of "no more than 16 bytes for structs", so it's probably a bad idea.

  1. Make a Clone method.

Have your Price implement a Clone method which returns a copy of the object - or alternately, create a copy-constructor that creates a new Price with the old values. Use that in your LINQ:

public class Price 
{
    // your fields

   public Price Clone()
   {
       return new Price
       {
           Symbol = this.Symbol,
           BidPrice = this.BidPrice,
           //etc.
       }
   }
}

var p = input.Prices.Where(x => x.Exchange == exchange).Select(x => x.Clone()).ToArray();
Avner Shahar-Kashtan
  • 14,492
  • 3
  • 37
  • 63
  • 1
    There is another reason to not use a struct, his class is mutable, having a mutable stuct can lead to very unexpected behavior, for example his `p.ForEach(x => x.AskPrice = 0);` would not actually update the price in `p` because `x` is another new copy of the value and would not cause the original sitting in `p` to change. – Scott Chamberlain Jul 11 '16 at 18:47