Setting property inside method [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 8 years ago.
Is something like the following possible or do you have to return the list and assign it afterwards? I get object reference not set to instance of an object.
public class MyCollection
{
public List<SomeObject> Collection { get; set; }
public List<SomeObject> CreateCollection()
{
// Is there a way to set the Collection property from here???
this.Collection.Add(new SomeObject()
{
// properties
});
}
}
...
MyCollection collection = new MyCollection();
collection.CreateCollection();

Yes, you can use an object initializer:
public List<SomeObject> CreateCollection()
{
// You may want to initialize this.Collection somehere, ie: here
this.Collection = new List<SomeObject>();
this.Collection.Add(new SomeObject
{
// This allows you to initialize the properties
Collection = this.Collection
});
return this.Collection;
}
Note that this will still potentially have an issue - you are never initializing this.Collection in any code you're displaying. You will need to initialize it to a proper collection in your constructor or via some other mechanism.
It is also an odd choice to have a "Create" method that initializes the local variable and returns a List<T>. Typically, you'd do one or the other. A more common approach would be to place this code within the constructor:
public class MyCollection
{
public IList<SomeObject> Collection { get; private set; } // The setter would typically be private, and can be IList<T>!
public MyCollection()
{
this.Collection = new List<SomeObject>();
this.Collection.Add(new SomeObject
{
Collection = this.Collection
});
}
}
You could then use it via:
MyCollection collection = new MyCollection();
var object = collection.Collection.First(); // Get the first element
That being said, in general, there is no real reason to make a custom class for a collection like this in most cases. Just using a List<SomeObject> directly is typically sufficient.

It's completely possible - you just have to instantiate it first, before you can use it:
public List<SomeObject> CreateCollection()
{
this.Collection = new List<SomeObject>(); // this creates a new list - the default if you just define a list but don't create it is for it to remain null
this.Collection.Add(new SomeObject()
{
// whatever
});
}
Of course, as pointed out in a comment, if you want that function to return a list, it would have to actually return the list. Presumably you mean public void CreateCollection(), though, since that was your question, whether you actually had to return a list (answer: no).

You must initialize this.Collection before adding elements into it.
public List<SomeObject> CreateCollection()
{
this.Collection = new List<SomeObject>();
this.Collection.Add(new SomeObject()
{
// properties
});
}

You can use a list initializer in this case:
public class Person
{
public string Name { get; set; }
public string Firstname { get; set; }
}
class Program
{
public static List<Person> Collection { get; set; }
public static List<Person> CreateCollection()
{
return new List<Person>()
{
new Person() { Name = "Demo", Firstname = "Demo1"},
new Person() { Name = "Demo", Firstname = "Demo1"},
};
}
static void Main(string[] args)
{
Collection = CreateCollection();
}
}

Related

Auto-initialized property not using overridden setter [duplicate]

using System;
using System.Collections.Generic;
class Parent
{
public Child Child { get; set; }
}
class Child
{
public List<string> Strings { get; set; }
}
static class Program
{
static void Main() {
// bad object initialization
var parent = new Parent() {
Child = {
Strings = { "hello", "world" }
}
};
}
}
The above program compiles fine, but crashes at runtime with Object reference not set to an instance of the object.
If you notice in the above snippet, I have omitted new while initializing the child properties.
Obviously the correct way to initialize is:
var parent = new Parent() {
Child = new Child() {
Strings = new List<string> { "hello", "world" }
}
};
My question is why does the C# compiler not complain when it sees the first construct?
Why is the broken initialization valid syntax?
var parent = new Parent() {
Child = {
Strings = { "hello", "world" }
}
};
It's not broken syntax, it's you who uses an object initializer on a property that's simply not instantiated. What you wrote can be expanded to
var parent = new Parent();
parent.Child.Strings = new List<string> { "hello", "world" };
Which throws the NullReferenceException: you're trying to assign the property Strings contained by the property Child while Child is still null.
Using a constructor to instantiate Child first, takes care of this.
There is nothing wrong with the initialisation, but it's trying to initialise objects that doesn't exist.
If the classes have constructors that create the objects, the initialisation works:
class Parent {
public Child Child { get; set; }
public Parent() {
Child = new Child();
}
}
class Child {
public List<string> Strings { get; set; }
public Child() {
Strings = new List<string>();
}
}
You seem to misunderstand what the collection initializer does.
It is a mere syntactic sugar that converts the list in the braces into a series of calls to Add() method that must be defined on the collection object being initialized.
Your = { "hello", "world" } is therefore has the same effect as
.Add("hello");
.Add("world");
Obviously this will fail with a NullReferenceException if the collection is not created.
The second syntax is valid for readonly properties. If you change the code to initialise the Child and Strings properties in the respective constructors, the syntax works.
class Parent
{
public Parent()
{
Child = new Child();
}
public Child Child { get; private set; }
}
class Child
{
public Child()
{
Strings = new List<string>();
}
public List<string> Strings { get; private set; }
}
static class Program
{
static void Main()
{
// works fine now
var parent = new Parent
{
Child =
{
Strings = { "hello", "world" }
}
};
}
}
Referencing a null can`t always be checked for at compile time. Although the compiler sometimes warns of using a variable before it has been assigned. The compiler works correctly. This is a run-time error.
Note that this syntax can cause some unexpected results and hard-to-spot errors:
class Test
{
public List<int> Ids { get; set; } = new List<int> { 1, 2 };
}
var test = new Test { Ids = { 1, 3 } };
foreach (var n in test)
{
Console.WriteLine(n);
}
You might expect the output to be 1,3, but instead it is:
1
2
1
3

How to make a list property writable in object initializer but read only in other places?

Consider the following code snippet that does not compile.
class Class
{
public double Value { get; set; }
public int Frequency { get; set; }
}
class BoxAndWhisker
{
private readonly List<Class> _classes = new List<Class>();
public BoxAndWhisker()
{
Classes = _classes.AsReadOnly();
}
public IReadOnlyList<Class> Classes { get; }
}
class Program
{
static void Main(string[] args)
{
BoxAndWhisker baw = new BoxAndWhisker
{
Classes =
{
new Class{ Value=1,Frequency=20},
new Class{Value=2,Frequency=10}
}
};
}
}
I want the property Classes to be read only right after baw is instatiated. How to do so? In other words, Classes must be writable in object initializer but read only in other places.
Edit
I prefer object initializer to parameterized constructor.
Why not pass Classes via constructor? E.g.
class BoxAndWhisker {
public BoxAndWhisker(params Class[] items) {
Classes = null != items
? new List<Class>(items).AsReadOnly()
: throw new ArgumentNullException(nameof(items));
}
public IReadOnlyList<Class> Classes { get; }
}
Then
static void Main(string[] args)
{
BoxAndWhisker baw = new BoxAndWhisker(
new Class { Value = 1, Frequency = 20 },
new Class { Value = 2, Frequency = 10 }
);
...
}
Remove the
set;
From the properties within
Class
And make the Class have a Constructor which sets the initial values of the Properties, therefore they cannot be overwrote / changed
The "object initializer" syntax in C# has no semantic difference compared to a property value assignment.
You can read in the docs:
The object initializers syntax allows you to create an instance, and after that it assigns the newly created object, with its assigned properties, to the variable in the assignment.
So this:
var foo = new Bar { Baz = "baz" };
is completely equivalent to:
var temp = new Bar();
temp.Baz = "baz";
var foo = temp;
So you cannot restrict the property assignment the way you want.
The only solution is to use a constructor as proposed in the other answers.
You pass the IList<Class> instance to the BoxAndWhisker constructor and maintain a backing IReadOnlyList<Class> property
class BoxAndWhisker
{
public BoxAndWhisker(IList<Class> classes)
{
if (classes == null)
throw new ArgumentNullException(nameof(classes));
Classes = new ReadOnlyCollection<Class>(classes);
}
public IReadOnlyList<Class> Classes { get; }
}
The usage example
BoxAndWhisker baw = new BoxAndWhisker(new List<Class>
{
new Class {Value = 1, Frequency = 20},
new Class {Value = 2, Frequency = 10}
});

Initialize get-only collection in object initializer from existing collection

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; }
}

Initializing list property without "new List" causes NullReferenceException

using System;
using System.Collections.Generic;
class Parent
{
public Child Child { get; set; }
}
class Child
{
public List<string> Strings { get; set; }
}
static class Program
{
static void Main() {
// bad object initialization
var parent = new Parent() {
Child = {
Strings = { "hello", "world" }
}
};
}
}
The above program compiles fine, but crashes at runtime with Object reference not set to an instance of the object.
If you notice in the above snippet, I have omitted new while initializing the child properties.
Obviously the correct way to initialize is:
var parent = new Parent() {
Child = new Child() {
Strings = new List<string> { "hello", "world" }
}
};
My question is why does the C# compiler not complain when it sees the first construct?
Why is the broken initialization valid syntax?
var parent = new Parent() {
Child = {
Strings = { "hello", "world" }
}
};
It's not broken syntax, it's you who uses an object initializer on a property that's simply not instantiated. What you wrote can be expanded to
var parent = new Parent();
parent.Child.Strings = new List<string> { "hello", "world" };
Which throws the NullReferenceException: you're trying to assign the property Strings contained by the property Child while Child is still null.
Using a constructor to instantiate Child first, takes care of this.
There is nothing wrong with the initialisation, but it's trying to initialise objects that doesn't exist.
If the classes have constructors that create the objects, the initialisation works:
class Parent {
public Child Child { get; set; }
public Parent() {
Child = new Child();
}
}
class Child {
public List<string> Strings { get; set; }
public Child() {
Strings = new List<string>();
}
}
You seem to misunderstand what the collection initializer does.
It is a mere syntactic sugar that converts the list in the braces into a series of calls to Add() method that must be defined on the collection object being initialized.
Your = { "hello", "world" } is therefore has the same effect as
.Add("hello");
.Add("world");
Obviously this will fail with a NullReferenceException if the collection is not created.
The second syntax is valid for readonly properties. If you change the code to initialise the Child and Strings properties in the respective constructors, the syntax works.
class Parent
{
public Parent()
{
Child = new Child();
}
public Child Child { get; private set; }
}
class Child
{
public Child()
{
Strings = new List<string>();
}
public List<string> Strings { get; private set; }
}
static class Program
{
static void Main()
{
// works fine now
var parent = new Parent
{
Child =
{
Strings = { "hello", "world" }
}
};
}
}
Referencing a null can`t always be checked for at compile time. Although the compiler sometimes warns of using a variable before it has been assigned. The compiler works correctly. This is a run-time error.
Note that this syntax can cause some unexpected results and hard-to-spot errors:
class Test
{
public List<int> Ids { get; set; } = new List<int> { 1, 2 };
}
var test = new Test { Ids = { 1, 3 } };
foreach (var n in test)
{
Console.WriteLine(n);
}
You might expect the output to be 1,3, but instead it is:
1
2
1
3

C# Object Initializer : Set Property from another one

I have the following object where in my constructor I add a new Guid as the Id.
public class MyObject
{
public MyObject()
{
Id = Guid.NewGuid().ToString();
}
public String Id { get; set; }
public String Test { get; set; }
}
I want to do something like that in an object initializer :
var obj = new MyObject
{
Test = Id; // Get new GUID created in constructor
}
Is it possible?
No, you can't do that. You'd have to just set it in a separate statement:
var obj = new MyObject();
obj.Test = obj.Id;
The right-hand side of the property in an object initializer is just a normal expression, with no inherent connection to the object being initialized.
If this is something you regularly want to do with one specific type, you could add a method:
public MyObject CopyIdToTest()
{
this.Test = Id;
return this;
}
and then use:
MyObject obj = new MyObject().CopyIdToTest();
or with other properties:
MyObject obj = new MyObject
{
// Set other properties here
}.CopyIdToTest();
No -- you can't access an object's properties inside an initializer. The initializer is basically some syntactic sugar for programmers.
Consider situations like:
class Program
{
static void Main()
{
var Id = "hello";
var obj = new MyObject
{
Test = Id // Get new GUID created in constructor
};
}
}
The Id you'd assign (if your idea was valid, which again, it isn't) isn't necessarily the Id you'd be getting.

Categories

Resources