I'm having a problem with the delegate not delegating...
I have an object called Tweet that has a string text and an int score. I want to sort the array of tweet objects(twtArray) in order of the score.
This is the code I have:
Array.Sort(twtArray, delegate(Tweet twt1, Tweet twt2)
{
return twt1.score.CompareTo(twt2.score); //(twt1.score - twt2.score)
});
and it throws:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Array.FunctorComparer`1.Compare(T x, T y)
Whilst debugging, I noticed that the first comparison works but in the second comparison, twt2 is null. And it can't be null because I definitely have 8 elements in the array.
I've tried reversing twt1 and twt2 as well but makes no difference.
I also tried making my own comparison method in the Tweet class but again, same thing.
Any help would be appreciated!
Also, I dont think this is a duplicate of this question: List.Sort in C#: comparer being called with null object
because i tried all the possible solutions from this but it's not working. i've also searched a lot on google :(
Even if you have a Tweet[] with 8 elements some can be null:
Tweet[] twtArray = new Tweet[8]; // all instances are null
You: The Tweet[] is of size 20 and I can see that there are 8 Tweet objects in there (with correct text and score values) in the first line of my code.
So the array's size is 20 but only 8 are initialized? (see above)
Array.Sort needs to compare all with all others.
You could prevent it in this way:
Array.Sort(twtArray, delegate(Tweet twt1, Tweet twt2)
{
if(twt1 == null && twt2 == null) return 0;
if(twt1 == null) return -1;
if(twt2 == null) return 1;
return twt1.score.CompareTo(twt2.score);
});
When you're working with an array that's only partially filled, that indicates that you don't actually want an array, you want List<T>.
Using that, you can have a collection that contains 8 items, but can be later expanded to 20 efficiently. And when you call Sort() on such list, you're not going to have any problems with nulls.
Related
I have this loop here, which for each question it is supposed to create, it generates and then formats a 'worded question' from an array of questions, such as; 'What is the sum of {0} + {1}?'. This loop then formats it, adds the worded question and the answer to an array.
// Use for loop to create the correct amount of questions
for (int i = 0; i < Data.Questions.numQuestions; i++)
{
Random rnd = new Random();
Data.Questions.Sum sum = new Data.Questions.Sum();
// Create part one and part two of the question using random numbers
// ex. 3 + 5
// 3 = partOne, 5 = partTwo
int partOne = rnd.Next(Data.Questions.Sum.min, Data.Questions.Sum.max);
int partTwo = rnd.Next(Data.Questions.Sum.min, Data.Questions.Sum.max);
// Randomly select one of the word questions
string fullQuestion = Data.Questions.Sum.wordedQuestions[rnd.Next(0, Data.Questions.Sum.wordedQuestions.Length)];
// Format the string with the generated numbers
fullQuestion = string.Format(fullQuestion, partOne, partTwo);
// Set out-of-class variables to be displayed to the user
Data.Questions.Sum.questions[i] = fullQuestion;
Data.Questions.Sum.answers[i] = partOne + partTwo;
}
Both Data.Questions.Sum.questions and Data.Questions.Sum.answers are List<string>'s and List<int>'s.
However, when this loop is run, with i = 0, I am thrown;
System.ArgumentOutOfRangeException: 'Index was out of range. Must be
non-negative and less than the size of the collection. Parameter name:
index'
Does anyone know what I'm doing wrong? As far as I know lists are dynamic, and I've defined like this;
// Arrays containing all questions and answers
// used to display questions and check answers
public static List<string> questions = new List<string>();
public static List<int> answers = new List<int>();
Also, to clarify, I do not want to use .Add(), as I have a settings panel which when you hit apply, re-runs this loop so the questions are up to date to the current settings. I need the loop to override the previous values.
Edit:
When using arrays, the better option here, I get;
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
On assigning Data.Questions.Sum.answers[i], after assigning the array like so; public static int[] answers {};
If you can't .Add() in this lists - create a copy of this lists and .Add() there. Lists has special for that kind of thing: new List<T>(IEnumerable<T>)
If you need to dynamically scale the collection, but you also need to iterate over it multiple times, then you'll need to check whether it is large enough to either insert, or just update.
You can do this with an extension method such as this...
public static class ListExtentions
{
public static void AddOrUpdate<T>(this List<T> that, int index, T value)
{
if (that.Count > index)
{
that[index] = value;
}
else
{
that.Add(value);
}
}
}
...which can then be called like this...
list.AddOrUpdate(index, value);
...however you can make things easier for yourself if you know how many questions you are going to have to start with.
If the number of questions changes when your UI changes, then you will also have to deal with the issue have scaling down the collection to ensure old elements are removed, which is much simpler if you just re-instantiate the collections every time you need to regenerate the questions / answers.
This is likly to be cause of your problem,(I have asked for clarification in comments where you didn't reply).
Still putting this as an answer, as it is potential error spot and you need to fix this.
As you mentioned you are facing this exception on i=0. there are high chanced that this is every time case not any specific case.
If Data.Questions.Sum.questions is empty then, Data.Questions.Sum.questions[i] = fullQuestion; , will surely throw such exception. Same way for Data.Questions.Sum.answers too.
In such case, you must use .Add() to insert into list.
so your code should be,
if (Data.Questions.Sum.questions.Count > i)
Data.Questions.Sum.questions[i] = fullQuestion;
else
Data.Questions.Sum.questions.Add(fullQuestion);
But if they are not empty, it must not be the cause of this exception.
One more thing i have noticed in your code is Data.Questions.Sum.wordedQuestions.
Even if you have valid list (here Data.Questions.Sum.wordedQuestions) - as you have Length prop, it must be Array not list.
If it is empty, while doing this
string fullQuestion = Data.Questions.Sum.wordedQuestions[rnd.Next(0, Data.Questions.Sum.wordedQuestions.Length)];
this line will surely throw
An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
as you are trying to get 0th index's data from it.
So before fetching data from list or array, first you need to check if it does have data init, and also it does have that index which you are asking.
something like
string fullQuestion = string.Empty;
if (Data.Questions.Sum.wordedQuestions != null &&
Data.Questions.Sum.wordedQuestions.Length > 0)
{
//here the way you are creating random number,
// you are assured about index is present in array.
int indexForWordedQuestion = rnd.Next(0, Data.Questions.Sum.wordedQuestions.Length);
fullQuestion = Data.Questions.Sum.wordedQuestions[indexForWordedQuestion];
}
Someone has chosen to mark this as duplicate, and though I can appreciate trying to clean up a question asking community, the I have examined the "duplicated" material in detail prior to posting my question. As indicated in my question below, I show the outputs of the issue in question, which has no nulls.
I have a method I create based on a user selecting a spinner value that looks like this:
public void terrain_ItemSelected(object sender, AdapterView.ItemSelectedEventArgs e)
{
Spinner spinner = (Spinner)sender;
var ttype = string.Format("{0}", spinner.GetItemAtPosition(e.Position));
Console.WriteLine(ttype);
foreach (var item in Terrainstr) { Console.WriteLine(item); }
var index = Array.FindIndex(Terrainstr, x => x.Contains(ttype));
As you can see, the string ttype is simply the selection the user makes with the spinner.
I have a string array Terrainstr that's initialized before OnCreate like this:
string[] Terrainstr = new string[10];
I populate it later with string fragments based on a number of user selections.
You'll notice I have Console.WriteLine commands in the method. My (annotated) output when that method is used is:
Fast (ttype)
Stock (Terrainstr[0])
Slow
Slow Sand
Slow Sand Rocks Whoops
Slow Sand Mud Rocks Whoops
Slow Mud Rocks Whoops
Slow Mud Rocks (Terrainstr[6])
I want to find which indeces of Terrainstr contain ttype. In this case, it wouldn't fit it at all. I get a null exception using the command shown here:
var index = Array.FindIndex(Terrainstr, x => x.Contains(ttype));
Please advise!
Basically your array is of size 10 and you only have 8 elements in it (based on Console.WriteLine output) so the last two are nulls.
Array.FindIndex doesn't check if the element is null or not and pass it to your lambda where you try to run .Contains. If it's null then you get a NullReferenceExeception.
Filter you array and only do the FindIndex on non-null elements.
var index = Array.FindIndex(Terrainstr, x => x != null && x.Contains(ttype));
This question already has answers here:
Directly modifying List<T> elements
(6 answers)
Closed 8 years ago.
I am writing a function which is passed in a list which is partially filled. I'd like to set some of the fields within the list inside this function. I thought that passing it as a reference would allow me to do this, however, I get the following error:
Error 1 Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable
I am wondering what I might need to do to tell C# that I wish to have the option of modifying the contents of the list.
Here is a summarized version of my code:
public static void Determine_RTMM_Descriptor(ref List<Struct_Descriptor_Type> symbols, string Dwarf_Output_Filename)
{
...
lines = System.IO.File.ReadAllLines(Dwarf_Output_Filename);
//loop on symbol names
for (int idx = 0; idx < symbols.Count; idx++)
{
if(symbols[idx].size == 0)
symbols[idx].size = (int)new System.ComponentModel.Int32Converter().ConvertFromString(split_line[DwarfInterface.SIZE_INDEX]);
...
}
Thanks in advance for any help.
The underlying issue here is that you have a list of value types. When you use the indexer of the list to get an item from the list you are getting a copy of that type. The code symbols[idx] is the value of that item. It is not a variable representing that item, as the error message is telling you.
You're trying to mutate the size of the copy, which will have no effect on the item of the list. This is such a common mistake that the compiler even makes this an error.
If you really are sure that you want to have a mutable value type (hint: you aren't, and you shouldn't have one; you almost certainly just want to have a class here to avoid this problem entirely) then you would need to get the value of the item, mutate it, and then set the item again:
if(symbols[idx].size == 0)
{
var symbol = symbols[idx];
symbol.size = 42;
symbols[idx] = symbol;
}
Your return type on the function is "void" when you should set the return type to the list. That should allow you to change it and return it modified.
[TestFixture]
class HashSetExample
{
[Test]
public void eg()
{
var comparer = new OddEvenBag();
var hs = new HashSet<int>(comparer);
hs.Add(1);
Assert.IsTrue(hs.Contains(3));
Assert.IsFalse(hs.Contains(0));
// THIS LINE HERE
var containedValue = hs.First(x => comparer.Equals(x, 3)); // i want something faster than this
Assert.AreEqual(1, containedValue);
}
public class OddEvenBag : IEqualityComparer<int>
{
public bool Equals(int x, int y)
{
return x % 2 == y % 2;
}
public int GetHashCode(int obj)
{
return obj % 2;
}
}
}
As well as checking if hs contains an odd number, I want to know what odd number if contains. Obviously I want a method that scales reasonably and does not simply iterate-and-search over the entire collection.
Another way to rephrase the question is, I want to replace the line below THIS LINE HERE with something efficient (say O(1), instead of O(n)).
Towards what end? I'm trying to intern a laaaaaaaarge number of immutable reference objects similar in size to a Point3D. Seems like using a HashSet<Foo> instead of a Dictionary<Foo,Foo> saves about 10% in memory. No, obviously this isn't a game changer but I figured it would not hurt to try it for a quick win. Apologies if this has offended anybody.
Edit: Link to similar/identical post provided by Balazs Tihanyi in comments, put here for emphasis.
The simple answer is no, you can't.
If you want to retrieve the object you will need to use a HashSet. There just isn't any suitable method in the API to do what you are asking for otherwise.
One optimization you could make though if you must use a Set for this is to first do a contains check and then only iterate over the Set if the contains returns true. Still you would almost certainly find that the extra overhead for a HashMap is tiny (since essentially it's just another object reference).
I have this 2d array or struct
public struct MapCell
{
public string tile;
}
public MapCell[,] worldMap;
But there's no way to check if key pair is exists in this array or not... No methods for that available.
I tried to do it like this
if (worldMap[tileX, tileY] != null) {
}
it doesnt work:
Error 1 Operator '!=' cannot be applied to operands of type 'Warudo.MapCell' and '<null>'
and for
if (worldMap[tileX, tileY].tile != null) {
it doesn't work either (exception pops up when it hits non existing element).
Index was outside the bounds of the array.
So, how do I check if key pair is exists or not?
You never mentioned which error you are getting -- array out of bounds or a null reference. If you are getting array out of bounds you should precede your null check with something along the lines of...
// make sure we're not referencing cells out of bounds of the array
if (tileX < arr.GetLength(0) && tileY < arr.GetLength(1))
{
// logic
}
Of course, it's best to just store the maximum array bounds instead of getting their lengths each time.
I also second (third?) the recommendation for using a class and not a struct.
Edit: Are you ever actually initializing this field? You haven't included it in your code sample. For example worldMap = new MapCell[100,100];, and then fill up the array...
If you're using an array of struct values, they always exist (once the array is constructed), but have their default value until you set them.
I would recommend using a class here instead of a struct. This will allow you to check for null, as well as act more in an expected fashion if you're going to be changing values (which, given the names, I expect...)
That being said, you could check for whether the string within the struct is null:
if (worldMap[tileX, tileY].tile != null)
{
// You've set the "tile" field inside of this "cell"...
This works because the default value of a struct is initialized with all references, including strings, to null.