Well, i have a simple List of classes which might get updated during run-time the problem is that when i set a property it doesn't update in the List.
Code:
class Foo
{
public List<Link> Link => new List<Link>();
public Foo()
{
//... Code that adds items to the list.
var addr = Link.Find(_ => _.Valid).Use().Address;
//here if i break and look at the Link list it must contain an item with Valid = false, yet it doesn't
}
}
public class Link
{
public Uri Address { get; set; }
public bool Valid = true;
public Link Use()
{
Valid = false;
return this;
}
}
i know that it must update because i am using a reference of that item in the list, yet for some unknown reason it doesn't.
Each time you are getting the value of Link property new list instance is created and returned. You should initialize property only once and return the same instance of the list when you are getting property value:
public List<Link> Link { get; } = new List<Link>();
Why your code do not work? Think about what happens when you add two items this way and perform a search:
Link.Add(new Link());
Link.Add(new Link());
Link.Find(...)
You are calling getter of Link property which creates new instance of the list and returns that empty instance.
You are adding first Link object to the instance of the list which you are received in step #1.
You are calling getter of Link property again, which creates new instance of the list (yes, another instance) and returns that new instance (empty list).
You are adding second Link object to the new empty list return on step #3.
You are calling getter of Link property which creates third instance of the list (again, empty) and returns that instance.
You are calling Find method of the empty list returned on step #5.
Related
I am not sure, but it seems i can not set a Property of a List?
The code in the (set) section does not get executed, debugger does not stop in the set when placing a break-point.
if a list property is dealt with differently? and if there is a link of some reading on this subject.
class test
{
public test()
{
id = new List<string>();
_id = new List<string>();
}
private List<string> _id;
public List<string> id
{
get { return _id; }
set
{
_id = value;
}
}
}
test t = new test();
t.id.Add("one");
The code in the (set) section does not get executed, debugger does not stop in the set when placing a break-point.
It appears that your question is, "why is the set accessor not called when I do: t.id.Add("one");"
The reason is simply that the set accessor is for the List itself. Your code is not setting t.id to a new List<string>, rather it is calling the .Add method of the object returned from the t.id property.
So the execution would look something like:
The get accessor is called when you do t.id, and it returns the instance of _id
The .Add method of _id is then called, and the string "one" is added to the private backing field _id
Note that in your existing code, the set does get called from the constructor, when you explicitly set the id to a new List.
You've stated that your goal is "to change some other properties when I add an item to id". There are a few ways to do this, but probably the simplest to understand and implement is:
In the get accessor for the list, return a copy of the list. This prevents clients from adding an item directly through the Add method. Also note that there is no need for a set accessor.
Provide a separate AddId method on your class, which is what the clients would have to call to actually add an item. This is where you have the opportunity to do something else when items are added to the list.
For example:
class Test
{
private readonly List<string> id = new List<string>();
// Return a copy of our private list
public List<string> Id => id?.ToList();
public void AddId(string newId)
{
id.Add(newId);
// do something else here when we add a new item
}
}
Note that this might cause some confusion for clients of your class, because they can still do test.Id.Add("invalid item"); and they won't get any error, but it also won't add anything to the private list. If they want to add something to the list, they have to call the AddId method:
t.AddId("one");
Code:-
(Note:- here I'm using read-only word means that property has only get accessor.)
Class Test
{
public List<string> list {get;}
public string name{get;}
public Test ()
{
list =new List<string>();
}
}
Main()
{
Test test =new Test();
test.list.add("c#"); //no error
test.name="Jhon"; //here I get compilation because property name is read-only
}
If you see above snippet. Test class contain two property which is name and list. In main method I'm creating object of test class to access these properties. So if you see if I try to set value to name property then I'll get compilation error because name property is read only. Similarly if you see another property 'list' that is also read-only if I use add property of List class then without error I'm able to add in list.
So I'm not getting how this happen.
That's because the set would refer to setting the List object, the actual instance of the collection. The List itself, when returned, is not readonly. If you want it to be readonly, you can do something like:
private List<string> list;
public ReadOnlyCollection<string> List {get => list.AsReadOnly()}
You have a misunderstanding of how a "read only" property would work.
If your code looked like this:
Test test = new Test();
test.list.Add("c#"); //no error because you are not 'setting' the object
test.list = new List<string>(); //Error here because you ARE setting the object
Add() is just a method of a List<T>, you are modifying the object not setting the property to something else.
If you want your collection to be "read only" you can use the ReadOnlyCollection interface. You can manage a private list internally and only expose through the public ReadOnlyCollection. Your desired functionality was never made clear so I wouldn't know what to suggest beyond what I have.
This is because in case of a string you return a copy of the instance - you can not assign to it.
Why .NET String is immutable?
In case of a List<T> you return a reference to an instance, which is not constant in your case - it is possible to change it.
To prove that yourself, you can do something like :
class Test
{
private string val;
public ref string Val {get {return ref val;}}
}
void Main()
{
Test t = new Test();
t.Val = "a";
Console.WriteLine("t.Val is - " + t.Val);
}
Observe special ref keyword I used in string property, to denote that string reference has to be returned and not a copy of it.
C# Concepts: Value vs Reference Types (Joseph Albahari)
public List<string> list {get;}
That means, it causes the error if you do the same action with name.
test.list = new List<string>();
test.list to get the list object and you call the method Add of the list object. So it's normal.
In the code below I have a property in base class which returns a list of custom objects. In the parent class I override this property and in the definition of the override I access the reference to the list of custom objects from the base class and add 2 objects to it.
Before returning I put a breakpoint in the code and check the content of the base property and notice that the two new objects are not there. Then I tried storing the reference to the list of objects in the base class locally and added two objects in the list again. I notice that in the local reference the 2 new objects have been added.
However, using both methods I'm pointing to the same reference so I should be able to add objects by referring to the base.TestProperty. Any idea why that won't work?
public override List<CustomObject> TestProperty
{
get
{
List<CustomObject> temp = base.TestProperty;
CustomObject obj1 = new CustomObject()
{
Name = "My Name"
};
CustomObject obj2 = new CustomObject()
{
Name = "Your Name"
};
// Adding to the base list
base.TestProperty.Add(obj1);
base.TestProperty.Add(obj2);
// Adding to temp list, which still points to the base list
temp.Add(obj1);
temp.Add(obj2);
// Base object doesnot contain obj1 and obj2, but the temp object does.
return base.TestProperty;
}
}
This isn't really the specific answer you're looking for, but... you should really reconsider your design.
You've got a property in your subclass... and getting that property changes your class' values. That's extremely counterintuitive. It's not like you'd expect:
Color bgCol = Color.Red;
int red = bgCol.R;
... that second statement to change values of your variable just by accessing one of its properties! How confused would you be if, when running that second statement, it changed the contents of bgCol to yellow?
My advice? Have the base class return what it's supposed to - forgetting about the subclass. And if your subclass needs to add values to that result? Then have it add the values when the subclass's property is called - but only to the result it's passing back - don't have it mess with the base object's properties at all.
public override List<CustomObject> TestProperty
{
get
{
List<CustomObject> objectsFromBase = base.TestProperty;
List<CustomObject> objectsFromThisClass = GetMySubclassCustomObjects();
List<CustomObject> retVal = new List<CustomObject>();
retVal.AddRange(objectsFromBase);
retVal.AddRange(objectsFromSubclass);
return retVal;
}
}
private List<CustomObject> GetMySubclassCustomObjects()
{
// your code for those two CustomObjects, and returning them from a list
}
Recently, I came across some code that looked like this:
public class Test
{
public ICollection<string> Things { get; set; }
public Test()
{
Things = new List<string> { "First" };
}
public static Test Factory()
{
return new Test
{
Things = { "Second" }
};
}
}
Calling Test.Factory() results in a Test object with a Things collection containing both "First" and "Second".
It looks like the line Things = { "Second" } calls the Add method of Things. If the ICollection is changed to an IEnumerable, there is a syntax error stating "IEnumerable<string> does not contain a definition for 'Add'".
It is also apparent that you can only use this kind of syntax in an object initialiser. Code such as this is invalid:
var test = new Test();
test.Things = { "Test" };
What is the name of this feature? In which version of C# was it introduced? Why is it only available in object initialisers?
It is called a collection initializer and it was added in the C# 3 language specifications (section 7.5.10.3 at introduction, section 7.6.10.3 in the current specs). To be specific, the code you use uses an embedded collection initializer.
Collection initializer actually just call the Add method, which is required according to the specs.
As Quantic commented, the specs say:
A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property.
That explains your unexpected results quite good.
Why is it only available in object initialisers?
Because it doesn't make sense elsewhere. You could just call the Add method yourself instead of using initializers for something else than initializing.
As Patrick already mentioned the collection initializer sequentially calls Add on the list. This assumes your property has been initialized by a constructor accordingly:
public class MyClass
{
public List<MyType> TheList { get; private set; }
public MyClass()
{
this.TheList = new List<MyType>();
}
}
If there is no such constructor that initializes your list you'll get a NullReferenceException in the following statement:
test.Things = { "Test" };
However this is something different than the following:
var m = new MyClass { TheList = new List<MyType> { ... } };
in which case you'll access the properties setter. Having none (or only a private one as in my example) leads to a compiler-error.
I need some clarification. Are these two methods the same or different? I get a little bit confused about when the reference to an object passed in a parameter by value is updated and when a new one is created. I know if that assignment creates a new reference, but what about changing a property? Will both of these methods update the field "_someObjectList" the same way?
public class SomeObject{
public Guid UniqueKey { get; set; }
public object SomeProperty{ get; set; }
}
public class SomeObjectListWrapper{
public SomeObjectListWrapper(List<SomeObject> someObjectList){
_someObjectList = someObjectList;
}
private readonly List<SomeObject> _someObjectList;
public void ReplaceItemPropertyValue1(Guid itemUniqueKey, object propertyValue)
{
List<int> resultIndices = new List<int>();
for (var i = 0; i < _someObjectList.Count(); i++)
{
if (_someObjectList[i].UniqueKey == itemUniqueKey)
resultIndices.Add(i);
}
if (resultIndices.Count != 1)
throw new Exception(
"just pretend this is the same exception as Single() throws when it can't find anything");
_someObjectList[resultIndices[0]].SomeProperty = propertyValue;
}
public void ReplaceItemPropertyValue2(Guid itemUniqueKey, object propertyValue)
{
_someObjectList.Single(x=>x.UniqueKey==itemUniqueKey).SomeProperty=propertyValue;
}
}
Because SomeObject is a class (ie. a reference type), both ReplaceItemPropertyValue methods are updating the same object as was inserted into the list and will be retrieved from the list later. (If SomeObject was a struct/value type, the compiler would prevent you from updating an rvalue/return value [1].)
As a minor side-note, your two methods are not actually identical. The Single method raises an exception if there is more than one matching item in the sequence. To properly match the behaviour, use First instead.
"rvalue" is not actually short for "return value," it just happens that in this case your rvalue is a return value, which is why I specified both options.
They may do the same thing depending on the data in your list.
ReplaceItemPropertyValue2 uses the Single method which will throw an exception if itemUnqiueKey is not found or found more than once.
But as long as itemUniqueKey can't appear more than once in the list, the two functions should accomplish the same task.
Both may be same.
The algorithm in the for loop set the object when key matches and then breaks out.
While the LINQ statement will set the object to all entries whose key match. It depends if your collection has same key entered more than once.