Issue with private set in list properties - c#

I have this piece of code:
public List<IVehicle> Vehicles { get; private set; }
My question is even though i am using a private set, why i can still add values to this list.

With private Set, you can't set the list to some new list from outside of your class. For example if you have a this list in a class:
class SomeClass
{
public List<IVehicle> Vehicles { get; private set; }
}
then while using:
SomeClass obj = new SomeClass();
obj.Vehicles = new List<IVehicle>(); // that will not be allowed.
// since the property is read-only
It doesn't prevent you assessing the Add method on the list. e.g.
obj.Vehicles.Add(new Vehicle()); // that is allowed
To return a Read-Only list you may look into List.AsReadOnly Method

Because private set; will not allow you to set list directly but you still can call methods of this list as it is using getter. You may want to use next:
//use this internally
private List<IVehicle> _vehicles;
public ReadOnlyCollection<IVehicle> Vehicles
{
get { return _vehicles.AsReadOnly(); }
}

.Add() is a function on the class List<> so after you get the list you can call the function. You can't replace the list with another one.
You could return an IEnumerable<IVehicle> that would make the list (sortof) readonly.
Calling .AsReadOnly() on the list would result in a really readonly list
private List<IVehicle> vehicles;
public IEnumerable<IVehicle> Vehicles
{
get { return vehicles.AsReadOnly(); }
private set { vehicles = value; }
}

When use use a private set what that means is that the property itself is un-setable from outside the class, not that it's methods are not available, and List<T>.Add() is only a method that the compiler knows nothing about.
By example:
public class VehicleContainer{
public List<IVehicle> Vehicles { get; private set; }
...
}
....
VehicleContainer vc = new VehicleContainer();
vc.Vehicles = new List<IVehicle>() // this is an error, because of the private set
int x = vc.Vehicles.Count; // this is legal, property access
vc.Vehicles.Add(new Vehicle()); //this is legal, method call
Take a look at this question, where use of the ReadOnlyCollection class is explained in the case when you want to restrict access to the collection itself, as well as the reference to the collection.

Getters and setters works on instances; not on properties of instances. An example;
Vehicles = new List<IVehicle>(); //// this is not possible
but if there is an instance it is possible to change its properties.

You can only instantiate it inside the containing class / struct of the List<IVehicle>. But once you have an instance, you can add items to it even outside, since the object is publicly visible.

Related

what is an optimal way to approach this list to array problem

class ProjectGroup
{
private List<string> members = new List<string>();
public List<string> Members { get { return members; } }
}
class Course
{
public bool AddStudent(string name)
{
ProjectGroup projectGroup = new ProjectGroup();
projectGroup.Members.Add(name);
return true;
}
}
So in this code I created a private list of members and accessed it with a public list Members which makes no sense for being a private in the first place. So instead I made a clone of the private list ToArray().
class ProjectGroup
{
private List<string> members = new List<string>();
public string[] Members { get { return members.ToArray(); } }
}
but that means I can't use projectGroup.Members.Add(name); anymore since Members is an array now.
How can I add string name to projectGroup.Members now?
The first code makes perfect sense. It's a read-only property so you can get the List object in order to add items or whatever but you cannot set the property, so you cannot replace the existing List with a completely different one. The thing is, you don't need the field at all. Just use the property:
public List<string> Members { get; } = new List<string>();
There will be a field created implicitly by the compiler but you don't need to use it so you don't need to declare it explicitly.
but that means I can't use projectGroup.Members.Add(name); anymore since Members is an array now.
How can I add string name to projectGroup.Members now?
It depends, you should ask yourself, will this ProjectGroup.Members changed over time or only populated once at creation time? Is the encapsulation actually worth the trouble?
Populated Once
If it populated once, you can use constructor. This way you can ensure the members is read-only.
class ProjectGroup
{
private List<string> members;
public string[] Members { get { return members.ToArray(); } }
public ProjectGroup(List<string> projectMembers)
{
//Ensure projectMembers cant be null
if(projectMembers == null)
throw new ArgumentNullException("projectMembers");
members = projectMembers;
}
}
You can then create an instance of the ProjectGroup this way:
var pg = new ProjectGroup(new List<string>(){"robert", "bob"});
Restricting Operations
If you want to limit the number of action you can do on the List<string>, you can add methods to expose the functionality you required. For example, let's say we want to validate name before being added into the members. You can add a method in ProjectGroup to do so (lets call it AddName).
class ProjectGroup
{
private List<string> members = new List<string>();
public string[] Members { get { return members.ToArray(); } }
public void AddName(string name)
{
//Ensure name is never empty string or null
if(string.IsNullOrWhiteSpace(name))
throw new ArgumentNullException("name");
members.Add(name);
}
}
You can then add more members as such:
var pg = new ProjectGroup();
pg.AddName("alice");
pg.AddName("sarah");
You can create method for member removal in the similar fashion.

I can't get value of base class field from child class object in c#

using System;
public class Program
{
public static void Main()
{
CloudCollectionHelper cloudHelper = new CloudCollectionHelper();
SlackHelper slackHelper = new SlackHelper();
cloudHelper.DatabaseID=12345;
Console.WriteLine(slackHelper.GetSlackPageTokens());
}
class CloudCollectionHelper
{
public long DatabaseID { get; set; }
}
class SlackHelper:CloudCollectionHelper
{
public long GetSlackPageTokens()
{
return DatabaseID;
}
}
}
current output: 0
Expected Output: 12345
I need output 12345 because DatabaseID from the cloudhelper so i need that databaseID in the slackhelper.
this is my c# online compiler: https://dotnetfiddle.net/QNQeEX
The child class does not get the assigned values from the base class. Becouse there can be mutliple instances from it. For example, if you have
...
CloudCollectionHelper cloudHelper1 = new CloudCollectionHelper();
CloudCollectionHelper cloudHelper2 = new CloudCollectionHelper();
cloudHelper1.DatabaseID = 1234;
cloudHelper2.DatabaseID = 6789;
Console.WriteLine(slackHelper.GetSlackPageTokens()); //It would not know, what value to use.
...
The best way would be assigning the value directly to the child class or using the static modifier.
Edit:
Best way if you need to take this value from the child class for whatever reason would be doing something like this:
...
CloudCollectionHelper cloudHelper = new CloudCollectionHelper();
cloudHelper.DatabaseID = 12345; //First assign the needed Value
SlackHelper slackHelper = new SlackHelper(cloudHelper); //then create a new instance from the child class
...
and add the constructor from the SlackHelper child class like this:
class SlackHelper:CloudCollectionHelper
{
public SlackHelper(CloudCollectionHelper cloudHelper)
{
this.DatabaseID = cloudHelper.DatabaseID;
}
... //Do everything else what this class needs here
}
If the Value from cloudHelper.DatabaseID can update during the runtime, you will need a event to update the child class. It still isn't the best way to do this, because the DatabaseID value is already public and can be accessed without the child class.
Edit 2:
Like I already told you in the comments, you could also avoid this problem with the static modifier. But this will effect every instance made from the CloudCollectionHelper class. As soons as you make it static, this will only hold 1 possible value for all instances.
(Please keep in your mind to use a comment if needed or best case, just avoid Magic numbers)
The property of your Object slackhelper has not been affected.
You don't need to create a CloudCollectionHelper Object.
SlackHelper slackHelper = new SlackHelper();
slackHelper.DatabaseID=12345;
Console.WriteLine(slackHelper.GetSlackPageTokens());
You should create this property inside the class to allow to read the Database ID
public long GetSlackPageTokens()
{
return base.DatabaseID;
}

can we access properties from constructor

I am working on a CSharp code where in constructor i need to access properties of that class. Logically it looks troublesome to me since i will be accessing properties of the object that hasn't is still under construction.
Its an old code using c# version 4.0 and i am kind of refactoring it so that's why can't redesign everything from scratch.
Thanks
class employee
{
employee()
{
int square = count * count;
}
private int count {get;set;}
}
There is nothing wrong with that, except that count will always be 0.
There is (almost) no such thing as a "partially-constructed" object in .Net, except for an object that hasn't set all of its state in the constructor.
If you're constructing the class, and none of the properties have been set previously in the constructor and none of the properties are static and set elsewhere, the values will be default or null, so there's no point getting what they contain. Otherwise, the constructor is the perfect place to set your properties to something.
At construction time you may set a property, but unless it has a static member backing the getting or is a value type, you will get a null value until you set it.
public class WhatClass
{
public WhatClass()
{
int theCount = Count; // This will set theCount to 0 because int is a value type
AProperty = new SomeOtherClass; // This is fine because the setter is totally usable
SomeOtherClass thisProperty = AProperty; // This is completely acceptable because you just gave AProperty a value;
thisProperty = AnotherProperty; // Sets thisProperty to null because you didn't first set the "AnotherProperty" to have a value
}
public int Count { get; set; }
public SomeOtherClass AProperty { get; set; }
public SomeOtherClass AnotherProperty { get; set; }
}
Yes, C# allow this, but sometime better to have private field which is wrapped by public property and in class method work only with field. In your case I would recommend to remove private property and use class field variable instead. If consumers of your class potentially may want to access a property - make it public with a private setter, this kind of autmatic property is an other alternative for privatr field wrapped by a property.

null exception with lists in c#

hey i am trying to work with a generic list in C# and for some reason after allocating memory for the list i am getting unhandeledNullException.
//edit
i found out what was my problem i did not use the properties currectly.
if lets say GeoInfo is a private member of my class, how do i do properties to it,
i tried :
private List<GeoInfo> GEOINFOS { get; set; } // edit i forgot to change it back
// but i want to have my geoinfos private and my properties public
thanks in advance for your help
You've made the properties private. If you want them to be public try:
public List<GeoInfo> GeoInfos { get; set; }
The auto-implemented value that is stored locally in the object will be private; but the properties themselves are public.
Because what you are declaring there are the property accessors.
If you want to write everything explicitly, you could do it the old pre 3.0 way
private List<GeoInfo> geoInfos = new List<GeoInfo>;
public List<GeoInfo> GeoInfos {
get { return geoInfos; }
set { geoInfos = value; }
}
This still relies on geoInfos being initialized somewhere (like the constructor) -- or nullPointerException will return.
You could do lazy-evaluation on it right in the getter:
private List<GeoInfo> geoInfos = new List<GeoInfo>;
public List<GeoInfo> GeoInfos {
get { if (geoInfos == null) {
geoInfos = new List<GeoInfo>;
}
return geoInfos;
}
set { geoInfos = value; }
}
This ensures that you don't have to specify a call in the constructor, and you don't have to worry about the execution sequence setting the element explicitly prior to getting it.
But if you use the auto-generated-properties, you will have to explicitly set the reference at some point. AS suggested elsewhere, the best bet is the constructor.
If you want a property to be private, use
private List<GeoInfo> GEOINFOS { get; set; }
However, there's not a lot of reason to use an auto property for a private member variable (and don't forget to initialize that list as well). If you want validation fine, but you're just using that property as a private variable.
Your null reference issue probably comes from not initializing the underlying property variable. That does not get done automatically, so
public MyClass()
{
GEOINFOS = new List<GeoInfo>();
}
One more thing: your naming convension for a property is odd for C#. How about keeping things consistent and sing GeoInfos?

Checking if any property has been assigned a value

I have a type SearchBag that holds a bunch of strings and nullable integers to use for passing on search values. I need a way to check if the search bag contains any values.
I'm currently trying to do it like this:
public bool HasValues()
{
return GetType().GetProperties().Any(p => p.GetValue(this, null) != null);
}
But was wondering if there's a better way.
Without modifying the SearchBag type, there isn't a better way.
EDIT: You could change the type to set a boolean flag in every property setter, then check the flag instead of using Reflection.
You could use Post Sharp to intercept the request to change a property value. You could have all search classes inherit from a common class with a List<string>. Then create an aspect attribute to add a property name to that dictionary whenever the value changes. The following is just a sample, and has bugs:
[Serializable]
public class PropertyChangeAwareAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionEventArgs eventArgs)
{
if (eventArgs.Method.Name.StartsWith("set_"))
((SearchBagBase)eventArgs.Instance).PropertiesChanged.Add(eventArgs.Method.Name);
base.OnEntry(eventArgs);
}
}
abstract class SearchBagBase
{
public List<string> PropertiesChanged = new List<String>();
}
[PropertyChangeAware]
class RegularSearch : SearchBagBase
{
public String Key { get; set; }
}
with usage:
RegularSearch regularSearch = new RegularSearch();
regularSearch.Key = "toys";
regularSearch.PropertiesChanged.ForEach(Console.WriteLine);

Categories

Resources