Friends, I would like to know why following code works for List<int> and not for List<string>. When I initialize list in getter of property then it does not work. If I initialize in constructor it works and also if I create list in the calling class it works.
public class MyClass
{
private List<int> _ints;
private static List<string> _strings;
public MyClass()
{
_ints = new List<int>();
}
public List<int> Ints
{
get
{
return _ints;
}
set
{
_ints = value;
}
}
public List<string> Strings
{
get
{
return _strings ?? new List<string>();
}
}
}
class Program
{
static void Main(string[] args)
{
MyClass mc = new MyClass();
// This works
// mc.Ints = new List<int>();
// This works
mc.Ints.Add(1);
// This does not
mc.Strings.Add("Krishna");
}
}
You aren't initializing the variable on the getter, if the variable is null you create a new list but you don't store the reference to it.
Change your code to this:
public List<string> Strings
{
get
{
if(_strings == null)
_strings = new List<string>();
return _strings;
}
}
This mc.Strings.Add("Krishna"); wont work because you return new List<string> every time you call mc.Strings. To explain it in more details :
MyClass mc = new MyClass(); // create new instance of MyClass and store it in the "mc" variable
mc // Direct call to the instance
.Strings // Direct call to MyClass Strings property
// inside Strings property :
// return _strings ?? new List<string>();
// meaning that if "_strings" member field is null
// return new instance of that property's type
.Add("abc"); // adds "abc" to the "forgotten" instance of List<string>
Simply speaking you're doing the same as you would call :
new List<string>().Add("abc");
To get this fixed you can use one-liner ( which you obviously tried ) like this one :
return _strings ?? ( _strings = new List<string>() );
Or use if statement :
if(_strings == null)
_strings = new List<string>();
return _strings;
Because List<int> initializes on the constructor of your class where as List<string> does not. Try:
public MyClass()
{
_ints = new List<int>();
_strings=new List<string>();
}
Your '_strings' list always null. If you call 'get' of 'Strings' property, you always create and return new list instance.
get
{
// if '_strings' is null, then create new instance of list
return _strings ?? new List<string>();
}
You need to use this:
get
{
// If '_strings' is null, then create new instance of list and assign new instance to '_strings'
return _strings ?? (_strings = new List<string>());
}
Inline variant of Gusman's answer, using some of the nice features of C# 6.0
public List<string> Strings => _strings ?? (_strings = new List<string>());
An FYI, to completely understand the problem, you could've put a breakpoint on this line:
return _strings ?? new List<string>();
You would've seen _strings is always null and you need to initialize it as answers above tell you so :)
Related
The way I understand it, C# properties are methods that have the get and set accessors.
class MyClass
{
private int x;
public int X
{
get
{
return x;
}
set
{
x = value;
}
}
}
I can call the property of the class in a script (and its accessors) with
MyClass mc = new MyClass();
mc.X = 10;
Debug.Log(mc.X); //returns 10
To my knowledge, however, I can pass only one value to the property.
Is there a way to pass arrays? Something like
MyClass mc = new MyClass();
mc.X = new int[] { 1, 2 }; //throws an error
Debug.Log(mc.X[0]); //I'd like it to return 1
This throws an error of course. I wonder if it's possible to do it any other way.
The solution is simple - use int[] instead of int
class MyClass
{
private int[] x;
public int[] X
{
get
{
return x;
}
set
{
x = value;
}
}
}
Also you might consider using auto property instead just like this:
class MyClass
{
public int[] X { get; set; }
}
You might also want to take a look at the Lists and read some basics ;)
Sure, just make the property an array or list also:
class MyClass
{
// in general a list should never be null, but could be empty, or without values.
// thats why we initialize the field here
private List<int> x = new List<int>();
public List<int> X
{
get
{
return x;
}
set
{
x = value;
}
}
}
then you could do:
var obj = new MyClass();
obj.X.Add(3);
obj.X.Add(6);
// (or use AddRange() to add another list or array of values
// Then loop the list and output values:
foreach(int x in obj.X)
{
Console.WriteLine(x);
}
Here's a dotnetfiddle for the above code:
https://dotnetfiddle.net/T2FrQ0
I have a class with a get-only collection property. I would like to initialize the collection with the values from an existing collection.
I know that it is possible to initialize the collection using a collection initializer. I could also create the object and then use AddRange on the collection to add the items of the existing collection. This would however create the object with an empty list and add the existing items afterwards.
Is there a way to create the object with the List properly initialized in the first place (without adding a constructor, of course)?
using System.Collections.Generic;
namespace EmptyConsoleApp
{
internal class Program
{
public static void Main(string[] args)
{
// Compiles, but is not what I need
var firstHolder = new Holder()
{
TheList = {"A", "B"}
};
// Compiles, but initializes the list after object creation
var existingList = new List<string>() {"Foo", "Bar"};
var secondHolder = new Holder();
secondHolder.TheList.AddRange(existingList);
// Does not compile
var thirdHolder = new Holder()
{
TheList = {existingList}
};
}
}
internal class Holder
{
public Holder()
{
TheList = new List<string>();
}
public List<string> TheList { get; }
}
}
No. You can't assign this read-only property from a collection initializer. It is read-only after all.
TheList = { "A", "B" } works since it calls Add on TheList (once for each item added), it doesn't create and assign a new instance, which it is not allowed to.
TheList = { existingList } doesn't work since there is a typing issue (TheList = { existingList[0] } does work).
The best option you have it to create a constructor parameter and drop your idea of using collection initializers for something it isn't fit for.
Is there a way to create the object with the List properly initialized in the first place (without adding a constructor, of course)?
No
It's not. That's what a constructor does. If you don't want to do it in the constructor, there is no way to do it.
it is not possible to initialize a read only property from outside of the class itself.
collection initializer is just a simplified syntax version and it does not mean using this syntax you have the same access as if you are in the class constructor
thirdHolder.TheList = existingList; // this is the traditional way
Perhaps you can use factory class pattern like this
internal class Program
{
public static void Main(string[] args)
{
// Compiles, but is not what I need
var firstHolder = new Holder()
{
TheList = { "A", "B" }
};
// Compiles, but initializes the list after object creation
var existingList = new List<string>() { "Foo", "Bar" };
var secondHolder = new Holder();
secondHolder.TheList.AddRange(existingList);
// Does not compile
//var thirdHolder = new Holder()
//{
// TheList = existingList
//};
//thirdHolder.TheList = existingList; // this is the traditional way
var thirdHolder = Holder.HolderFactory(existingList);
}
}
internal class Holder
{
public Holder()
{
TheList = new List<string>();
}
public static Holder HolderFactory(List<string> theList)
{
return new Holder(theList);
}
private Holder(List<string> theList)
{
this.TheList = theList;
}
public List<string> TheList { get; }
}
I would like to add a new item at the back of a list, and get the newly created item.
Let's assume that we could do something like this for one moment:
class Temp
{
public string First { get;set;}
public string Second { get;set;}
}
List<string> list = new List<string>();
var newItem = list.Push();
newItem.First="asd";
newItem.Second = "qwe;
this would be easier than
var newItem = new Temp();
newItem.First="asd";
newItem.Second = "qwe;
list.Add(newItem);
especially when I can't use auto-properties.
Is this possible?
Unless you implement your own List type and add the Push method, the only way you can do that is if the T in List can be constructed using a parameterless constructor.
Here's an extension method for that.
This is not recommended, but is an answer to your question.
Something along the lines of this - I did not compile or run this code
public static class ListEx {
public static T Push<T>(this List<T> list) where T: new() {
// Create an instance of T.
var instance = new T();
// Add it to the list.
list.Add(instance);
// Return the new instance.
return instance;
}
}
You can use object initializers:
var list = new List<Temp>();
list.Add(new Temp{ First = "abc", Second = "def" });
Or together with a collection initializer:
var list = new List<Temp> { new Temp{ First = "abc", Second = "def" } };
This turns your four liner into a one liner.
Or with more than one entry:
var list = new List<Temp> {
new Temp{ First = "abc", Second = "def" },
new Temp{ First = "ghi", Second = "jkl" },
new Temp{ First = "mno", Second = "pqr" }
};
And it should of course be a list of Temp instead of a list of string.
I have a question about assignment.
public class A {}
public class AHolder
{
public A AnInstance;
}
void Change()
{
A anotherInstance=new A();
anotherInstance.aField="bla";
A anotherInstance2=new A();
anotherInstance2.aField="blabla";
List<AHolder> aList= new List<AHolder>();
aList.add(new AHolder(){AnInstance=anotherInstance});
aList.add(new AHolder(){AnInstance=anotherInstance});
aList.add(new AHolder(){AnInstance=anotherInstance});
anotherInstance=anotherInstance2;
}
How can I implement the code that ensures the changes of all AnInstance values in aList, when anotherInstance changed without using loop?
Update:after executing the code lines above ,i'm trying to get "blabla" value from aList[0].AnInstance.aField.Is it possible?
You could do it using a wrapper class instance, instead of referencing directly to the AHolder instance, but think if you really need this extra indirection layer, as it would make your code less readable.
I expect the following sample explains how to do it:
public class MyData { public string Value; }
public class MyRef { public MyData Instance; }
void Change()
{
var dataFoo = new MyData() { Value = "foo" }
var dataBar = new MyData() { Value = "bar" }
var referer = new MyRef() { Instance = dataFoo }
var list= new List<MyRef>();
list.add(referer);
list.add(referer);
list.add(referer);
// for i=0 to 2 -> list[i].Instance.Value = "foo"
referer.Instance = dataBar;
// for i=0 to 2 -> list[i].Instance.Value = "bar"
}
private List<string> _S3 = new List<string>();
public string S3[int index]
{
get
{
return _S3[index];
}
}
Only problem is I get 13 errors. I want to call string temp = S3[0]; and get the string value from the list with the particular index.
You can't do that in C# - you can't have named indexers like that in C#. You can either have a named property, with no parameters, or you can have an indexer with parameters but no name.
Of course you can have a property with a name which returns a value with an indexer. For example, for a read-only view, you could use:
private readonly List<string> _S3 = new List<string>();
// You'll need to initialize this in your constructor, as
// _S3View = new ReadOnlyCollection<string>(_S3);
private readonly ReadOnlyCollection<string> _S3View;
// TODO: Document that this is read-only, and the circumstances under
// which the underlying collection will change
public IList<string> S3
{
get { return _S3View; }
}
That way the underlying collection is still read-only from the public point of view, but you can access an element using:
string name = foo.S3[10];
You could create a new ReadOnlyCollection<string> on each access to S3, but that seems a little pointless.
C# cannot have parameters for their properties. (Side note: VB.Net can though.)
You can try using a function instead:
public string GetS3Value(int index) {
return _S3[index];
}
You have to use this notation
public class Foo
{
public int this[int index]
{
get
{
return 0;
}
set
{
// use index and value to set the value somewhere.
}
}
}
_S3[i] should automatically return the string at position i
So just do:
string temp = _S3[0];
Try this
private List<string> _S3 = new List<string>();
public List<string> S3
{
get
{
return _S3;
}
}
I would just go with
class S3: List<string>{}