Null reference error when accessing an array - c#

I have the following class:
public class MyClass
{
public MyArrObj[] arrOfObj; //Array of objects
}
When I try to access it using:
MyClass myClass = new MyClass;
myClass.arrOfObj = GetData();
it gives me a null exception. If it wasn't an array, I could have new'd it, but i am not sure how to handle this.

When creating an array of a reference type (MyArrObj in your case), it is required that you allocate memory for each array element (using new operator) in addition to allocating memory for the array itself.
Array of Reference types
For example, if you create an MyArrObj array,
MyArrObj[] objectArray = new MyArrObj[3];
Then the array elements objectArray[0] to objectArray[2] would still be null. A separate initialization is required.
objectArray[0] = new MyArrObj();
Only when the above step is done can you access the members of the array element.
objectArray[0].SomeMethod();
SomePropertyType readProperty = objectArray[0].SomeProperty;
If you skip the array element initialization then trying to access a member of the array element would give a System.NullReferenceException
objectArray[0].SomeMethod(); // throws NullReferenceException
because
objectArray[0]; is null.
You can check this using
if(objectArray[0] == null)
{
Console.WriteLine("objectArray[0] is null");
}
Array of Value types
If you know the array before hand, then you can use the initialization pointed out by #clonked.
For value types like built in types(like int, float, structs, etc. you need not initialize each array element.
For example, if you create an int array,
int[] intArray = new int[3];
Then the array elements intArray[0] to intArray[2] have the memoery allocated and values can be assigned to them.
intArray [0] = 1;
Check your GetData()
So your GetData() method should include code like this,
private MyArrObj[] GetData()
{
int numberOfObjects = GetNumberOfObjects(); // Get the number of objects at runtime
MyArrObj[] objectArray = new MyArrObj[numberOfObjects];
for (int i = 0; i < numberOfObjects; i++)
{
objectArray[i] = new MyArrObj();
}
return objectArray;
}
More information
Types (C# Programming Guide)
Value Types (C# Reference)

The way your are using your GetData() method, it has to return an array.
Here's a couple of ways to create an array on the fly (you'll still need to populate them, though):
int[] x = new int[5] ; // create a 5-element array of integers
int[] y = (int[]) Array.CreateInstance(typeof(int),5) ; // create a 5 element array of integers
Cheers.

You need to do something like this:
class Program
{
static void Main(string[] args)
{
MyClass myClass = new MyClass();
myClass.arrOfObj = GetData();
foreach (var item in myClass.arrOfObj)
{
Console.WriteLine(item.ToString());
}
Console.ReadLine();
}
private static int[] GetData()
{
return new int[]{1,2,3,4,5};
}
}
public class MyClass
{
public int[] arrOfObj; //Array of objects
}
the GetData method is the important part.
it has to initialize a new array and return it.

If the signature of your GetData() method looks like public MyArrObj[] GetData() then your code should work correctly. I tested your code with 2 versions of the 'GetData' method:
public MyArrObj[] GetData()
{
return new MyArrObj[3] { new MyArrObj(), new MyArrObj(), new MyArrObj() };
}
public MyArrObj[] GetData2()
{
return new MyArrObj[0];
}
Neither of these methods threw a null reference exception. I would suspect there is something else inside of your GetData() method that is throwing the null reference, rather than the operation of assigning the results of that method to the arrOfObj field.

Related

Convert type List<object> to 'T'

I'm trying to write a generic function:
private T Convert<T>(List<object> list) where T : new()
{
if (typeof(T).IsArray)
{
T array = new T();
// ... convert here
return array;
}
}
In this scenario, I know that the objects in list should be the same type as the array of T. For example, if T is System.Int32[] then I'd expect the objects in list to all be Int32.
However, T is not always an array. In other cases, it may be a value type or even some other reference type.
How can I convert this List<object> to T given these assumptions?
UPDATE
This seems to be working within a set of given assumptions:
private T Convert<T>(List<object> list)
{
// ... code omitted for brevity
if (typeof(T).IsArray)
{
// big assumption here
Type elementType = Type.GetType(typeof(T).FullName.Replace("[]", string.Empty));
Array array = Array.CreateInstance(elementType, list.Count);
for (int i = 0; i < list.Count; i++)
{
array.SetValue(list[i], i);
}
return (T)Convert.ChangeType(array, typeof(T));
}
}
You can use the ancient ArrayList to create an array of the desired type. (It's pretty much the only thing it is good for.)
private T Convert<T>(List<object> list)
{
if (typeof(T).IsArray)
{
var tmp = new ArrayList(list);
return (T)(object)tmp.ToArray(typeof(T).GetElementType());
}
return (T)list.First();
}
Fiddle
Note: You should probably add some error handling, e.g. if the caller specified a T that doesn't make sense for the list.

Why the tuple-type list element's value cannot be modified?

In C# 8.0, I can modify the value inside a tuple directly by accessing the field name:
(string name, int score) student = ("Tom", 100);
student.name = "Jack";
Console.WriteLine(student);
And I can modify the list element's property as follow:
var list = new List<Student>(); // assume I have a Student class which has a Name property
list.Add(new Student { Name = "Tom" });
list[0].Name = "Jack";
Console.WriteLine(list[0]);
But why can't I modify the tuple-type element's value like this?
var list = new List<(string name, int score)>();
list.Add(("Tom", 100));
list[0].name = "Jack"; // Error!
Console.WriteLine(list[0]);
A tuple (ValueTuple) is a struct. Rather than returning a reference to the value as is the case with your Student example, you would actually recieve a copy of the tuple.
Changes to that copy wouldn't be reflected in the list and would be discarded. The compiler is smart enough to recognize this and stops you from doing it.
If it did compile, it would be to something similar to the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // still Tom
Mutable structs can be dangerous if you don't use them properly. The compiler is simply doing its job.
You can work around this with the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
list[0] = copy; // put it back
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // Jack
Try It Online
If you use an array (string, int)[] instead of a List<(string, int)>, this isn't a problem due to the way array element access works:
var arr = new (string name, int score) [] { ( "Tom", 10 ) };
arr[0].name = "Jack";
Console.WriteLine(arr[0].name); // Jack
Try It Online
This behavior is not unique to List or your tuple type. You'll experience this issue with any collection where the element is a Value Type (unless of course they offer a ref element accessor).
Note that there are similar issues when having a readonly field of a mutable value type that mutates via method calls. This can be much more insidious as no error or warning is emitted:
struct MutableStruct {
public int Val;
public void Mutate(int newVal) {
Val = newVal;
}
}
class Test {
private readonly MutableStruct msReadonly;
private MutableStruct msNormal;
public Test() {
msNormal = msReadonly = new MutableStruct(){ Val=5 };
}
public void MutateReadonly() {
Console.WriteLine(msReadonly.Val); // 5
msReadonly.Mutate(66); // defensive copy!
Console.WriteLine(msReadonly.Val); // still 5!!!
}
public void MutateNormal() {
Console.WriteLine(msNormal.Val); // 5
msNormal.Mutate(66);
Console.WriteLine(msNormal.Val); // 66
}
}
new Test().MutateReadonly();
new Test().MutateNormal();
Try It Online
ValueTuple is a great addition to the framework and language. But there's a reason you'll often hear that [Mutable] structs are evil. In the majority of cases you shouldn't hit these restrictions. If you find yourself falling into this pattern a lot, I suggest moving over to a record, which is a reference type (thus not suffering these issues) and can be reduced to a tuple-like syntax.
Mutable value types are evil, it's hard to see why this prints "Tom" not "Jack":
(string name, int score) student = ("Tom", 100);
(string name, int score) student2 = student;
student.name = "Jack";
Console.WriteLine(student2);
The reason is that you always create a copy. Because it's not obvious you should avoid mutable value types. To avoid that people will fall into that trap the compiler just allows to modify the object directly via properties(like above). But if you try to do it via a method call you get a compiler error "Cannot modify the return value of ... because it is not a variable".
So this is not allowed:
list[0].name = "Jack";
It would create a new copy of the ValueTuple, assigns a value but doesn't use or store it anywhere.
This compiles because you assign it to a new variable and modify it via property:
(string name, int score) x = list[0];
x.name = "Jack"; // Compiles
So it compiles but gives you again a suprising result:
Console.WriteLine(list[0]); // Tom
Read more about it here: Do Not Define Mutable Value Types

Alternative to using ref in foreach?

I have a modifying method with a signature like
private bool Modify(ref MyClass obj);
that will make modifications to obj and indicate succes with it's return value. Modify is not reassigning the reference (I know that this wouldn't work), just modifying instance fields, so I want to use it to do something like the following:
foreach(MyClass obj in myList)
{
bool success = Modify(obj);
// do things depending on success
}
I am running into a problem compiling as obj is "not being passed with the ref keyword". However, if I put the ref keyword in like so:
bool success = Modify(ref obj);
I get "cannot use obj as a ref/out because it is a 'foreach iteration variable". I understand that foreach uses an immutable iterator and that's why this doesn't work.
My question is what is the easiest alternative to make something like this work?
I have tried using
foreach(int i = 0; i < myList.Count; i++)
{
bool success = Modify(ref myList[i]);
// do things depending on success
}
but they I get "a property or indexer may not be passed as an out of ref parameter".
Thanks your help.
Any type within C# is passed actually by value. When you pass an instance of a class to a method what is actually passed is not the instance itself but a reference to it which itself is passed by value. So effectivly you're passing instances of a class as reference - which is why you call them reference-types.
In your case you just modify an existing instance referenced by that reference-value in your method, no need to use the ref-keyword.
foreach(var m in myList)
{
MyMethod(m);
}
MyMethod(MyClass instance)
{
instance.MyProperty = ...
}
If you'd really pass the reference by reference you'd re-assign the obj on every iteration within your loop which isn't allowed within a foreach-block. This would be similar to the following:
foreach(var m in myList)
{
m = new MyClass();
}
On the other side you could also use a classic for-loop. However you'd need a temporary variable to store the outcome of your method:
for(int i = 0; i < myList.Length; i++)
{
var tmp = myList[i];
MyMethod(ref tmp);
myList[i] = tmp;
}
You state
Modify is not reassigning the reference
Therefore, there is no reason the Modify(ref MyClass) function needs to pass argument by ref.
You should be able to do the same "modifications", whatever that is (please clarify that) by passing the object reference by value, i.e. removing the ref keyword.
So, the fix here should be changing your Modify function signature from Modify(ref MyClass) to Modify(MyClass)
use a temp variable to bypass the message
foreach(MyClass obj in myList)
{
MyClass objTemp = obj;
bool success = Modify(ref objTemp);
// do things depending on success
}
private MyMethod(ref MyClass instance)
{
instance.MyProperty = ...
}
It is solved by using LINQ.
My Code:
private static List<string> _test = new List<string>();
public List<string> Test { get => _test; set => _test = value; }
static void Main(string[] args)
{
string numString = "abcd";
_test.Add("ABcd");
_test.Add("bsgd");
string result = _test.Where(a => a.ToUpper().Equals(numString.ToUpper()) == true).FirstOrDefault();
Console.WriteLine(result + " linq");
}
You need to cast your array to a Span:
int[] nums = new[] { 1, 2, 3, 4, 5 };
int i = 0;
foreach (ref var item in nums.AsSpan())
{
item += 10;
Console.WriteLine(nums[i++]);
}
output:
11
12
13
14
15
You can modify the iteration variable because Span Enumerator's Current property is of ref T type.

displaying structs in an array using enumerator

In an object I have :
public IEnumerable<voiture> recup_voitures()
{
foreach (voiture v in _arrVCollection)
{
yield return (v);
}
}
voiture being a struct and _arrVCollection being an array containing some struct voiture.
In my main class I have :
foreach (CarCollection.voiture o in collection.recup_voitures())
{
//some code to display the content of each struct
}
What is happening is that if I have an array of length 5 and only 1 struct voiture in it, it will do the displaying code 5 times instead of only 1. What am I doing wrong?
If you are creating array of struct with length, say 5, it means array will contain 5 structs constructed using default constructor.
voiture[] arr = new voiture[5];
arr[0] = some_non-default_voiture;
In that case your array will contain one non-default voiture instance and four default instances.
To get only one voiture instance you can create array with length 1, or use List<T>:
List<voiture> = new List<voiture> { new voiture() };
If you really need to deal with array with length 5, consider to use nullable types:
voiture?[] _arrVCollection = new voiture?[5];
_arrVCollection[0] = new voiture();
And enumerate like this:
public IEnumerable<voiture> recup_voitures()
{
foreach (voiture? v in _arrVCollection)
{ if (v.HasValue)
yield return v.Value;
}
}
Edit:
As Servy mentioned, method recup_voitures() can be rewritten with linq:
public IEnumerable<voiture> recup_voitures()
{
return _arrVCollection.Where(x => x.HasValue).Select(x => x.Value);
}
In your current code you get exception.
You can change your code in recup_voitures methods if the voiture will be class:
public static IEnumerable<voiture> recup_voitures()
{
foreach (voiture v in _arrVCollection)
{
if (v != null)
yield return (v);
}
}

ArrayList of object references

Having a user defined class, like this:
class Foo
{
public int dummy;
public Foo(int dummy)
{
this.dummy = dummy;
}
}
And having then something like this:
ArrayList dummyfoo = new ArrayList();
Foo a = new Foo(1);
dummyfoo.add(a);
foreach (Foo x in dummyfoo)
x.dummy++;
How much is a.dummy?
How can i create my ArrayList so that a.dummy equals 2, meaning that my ArrayList contains basically pointers to my objects and not copies.
It is already 2, as Array/Collections (to be precise any .NET Class/reference type) are passed by reference by default.
In fact the reference variable is passed by value, but behaves as if passed by reference.
Why ->
Consider var arr = new ArrayList();
The above statement first creates an ArrayList object and a reference is assigned to arr. (This is similar for any Class as class are reference type).
Now at the time of calling,
example -> DummyMethod(arr) ,
the reference is passed by value, that is even if the parameter is assigned to a different object within the method, the original variable remains unchanged.
But as the variable points(refer) to same object, any operation done on underlying pointed object is reflected outside the called method.
In your example, any modification done in for each will be reflected in the arrayList.
If you want to avoid this behavior you have to create copy/clone of the object.
Example:
Instead of
foreach (Foo x in dummyfoo)
x.dummy++;
Use
foreach (Foo x in (ArrayList)dummyfoo.Clone())
x.dummy++;
It already contains references, not copies. When doing this:
Foo a = new Foo(1);
dummyfoo.Add(a);
a reference to a is passed, not a copy.
Hence, dummy will be 1 initially and then 2 after the increments.
Anyway, you're better off using generics:
List<Foo> dummyfoo = new List<Foo>();
Foo a = new Foo(1);
dummyfoo.Add(a);
foreach (Foo x in dummyfoo)
x.dummy++;
You declared Foo as a class. Classes are reference types. It already works like this. Give it a try:
class Program
{
static void Main(string[] args)
{
ArrayList dummyfoo = new ArrayList();
Foo a = new Foo(1);
dummyfoo.Add(a);
foreach (Foo x in dummyfoo)
x.dummy++;
Console.WriteLine(a.dummy); //Prints 2!
}
}
class Foo
{
public int dummy;
public Foo(int dummy)
{
this.dummy = dummy;
}
}
As an aside, a generic List<T> is preferred over the deprecated ArrayList type.
It is already 2: Here is your code on ideone that verifies that..
Unlike value types (i.e. structs), reference (i.e. class) objects are passed by reference.
P.S. Generics are available since C#2.0, so consider using List<Foo> in place of ArrayList for improved type safety.

Categories