This question already has answers here:
What is the default value of a member in an array?
(4 answers)
Closed 3 years ago.
I would like to ask some rather basic question (I presume) the answer to which seems to elude me. In the following code I am trying to load an array with a csv file (; separated) that contains two columns (string Name, int Score).
For simplicity I have commented out the loop I want to use to transfer this file onto an array and I am just loading the 2nd element. For some reason unless I use (scoreobj[1] = new HighScore();) I get a null reference.
Why do I need to do that? Haven't I already initialized the scoreobj[] object at the beginning?
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WindowsFormsApp1
{
public class HighScore
{
public string Name { get; set; }
public int Score { get; set; }
public static void LoadHighScores(string filename)
{
string[] scoredata = File.ReadAllLines("C:/Users/User/Desktop/Test.csv");
HighScore[] scoreobj = new HighScore[scoredata.Length];
scoreobj[1] = new HighScore();
scoreobj[1].Name = scoredata[1].Split(';')[0];
//for (int index = 0; index < scoredata.Length; index++)
//{
// scoreobj[index].Name = scoredata[index].Split(',')[0];
// scoreobj[index].Score = Convert.ToInt32(scoredata[index].Split(';')[1]);
//}
Console.WriteLine(scoreobj[1].Name);
}
}
}
Because just declaring an index of a specific size does not create any element of type HighScore,. Instead you just reserve some memory. In other words: just because you have a bag does not put any potatoes in it. You have to go to the market and put potatoes into your bag yourself.
You could even create an instance of a derived class and put it into the exact same array. How would the compiler in this case know which class you want to instantiate?
class Foo { ... }
class Bar { ... }
var array = new Foo[3]; // how would anyone know if you want three Foo-instances or 3 Bar-instances? or a mix?
The compiler can't know which type you want to instantiate and thus won't create those instances. So you have to create the instance yourself.
But even without a derived class, your constructor may have parameters, which compiler can't guess:
class HighScore
{
public HighScore(string name, int score) { ... }
}
var array = new HighScore[3]; // how to set the parameters for the instances???
That's why your object just contains no instances, but just the types default-value, which is null for reference-types.
HighScore[] scoreobj = new HighScore[scoredata.Length]; creates an array of the specified length (scoredata.Length), but containing no objects.
Yo need to assign an object at each index, which could be done as follows:
for (int i = 0; i < scoredata.Length; i++)
{
scoreobj[i] = new HighScore();
scoreobj[i].Name = scoredata[i].Split(';')[0];
}
Related
Consider this C# program:
using System;
using System.Collections.Generic;
using System.Linq;
namespace SandboxApplication
{
public class IntsOwner
{
private List<int> _ints;
public IntsOwner (IEnumerable<int> ints)
{
_ints = ints.OrderBy(i => i).ToList(); // They must be in the correct order
}
public IEnumerable<int> Ints
=> _ints;
public void CheckFirstTwoInts ()
{
if (_ints.Count < 2)
{
Console.WriteLine("You need to collect some more ints before trying this.");
}
else if (_ints[0] <= _ints[1])
{
Console.WriteLine("Your ints are in the correct order and you should stop worrying.");
}
else
{
Console.WriteLine("You've failed, your highness.");
}
}
}
class Program
{
static void Main (string[] args)
{
var intsOwner = new IntsOwner(new List<int> {1, 2, 3, 4, 5});
var ienumerable = intsOwner.Ints;
var list = (List<int>)ienumerable;
intsOwner.CheckFirstTwoInts();
list[0] = 6;
intsOwner.CheckFirstTwoInts();
Console.ReadLine();
}
}
}
If you run this, you will get two lines of output:
Your ints are in the correct order and you should stop worrying.
You've failed, your highness.
The original designer of the IntsOwner class wanted to make sure that a particular property (ordering of list elements) held for the private member _ints. But because a reference to the actual object is returned through the Ints property, a user of the class can modify the object so that this property no longer holds.
This sort of code doesn't appear very likely in practice, but it is still disconcerting that control of members intended to be private can "leak" in this way. How much, if at all, should programmers try to block this sort of thing off? For example, would it be reasonable or proportionate to change the Ints property's expression body to _ints.Select(i = i) and thereby close off this way of modifying the private member? Or would that be unnecessarily paranoid to the detriment of the code's readability?
I always add a call to AsReadOnly() after ToList
Immutability is key, not just principal of least knowledge in the return type
https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
You could do something like
public IEnumerable<int> Ints => _ints.ToList<int>();
so you're not returning the reference to _ints, but only a copied list. Anyone modifying the return value then would only be modifying their own copy of it, not the privately stored one.
I'm new to ArrayList. As far as I know ArrayLists each element(?) can contain multiple values (excuse me for not using the correct terminology).
Now, here's the problem, If I make an ArrayList where each element contains only one value, I can easily print it, however as in the example below, if I attempt to make an ArrayList where each element contains multiple values - I cannot print the values of each element in the ArrayList.
If I try to use foreach or for loop using Console.Writeline(list[i]); all I get is the namespace.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication13
{
class Register
{
public string name;
public string surname;
public void RegisterData()
{
Console.Write("Enter your name: "); name = Console.ReadLine();
Console.Write("Enter your surname: "); surname= Console.ReadLine();
}
public void PrintData()
{
Console.WriteLine("Name: " + name + " Surname: " + surname);
}
}
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
Register temp = new Register();
temp.RegisterData();
list.Add(temp);
for (int i = 0; i < list.Count; i++)
{
// How can I use the PrintData (without modifying it) method to print the ArrayList values?
}
}
}
}
How could I be able to print the values of the ArrayList without using the PrintData Method?
Thank you in advance!
ArrayList is .NET 1.1 stuff. You should use List instead. It's generic and superior over ArrayList.
So you define your list like
List<Register> list = new List<Register>();
...
list.Add(temp);
And in the foreach loop, you can access the properties of your Register class. You can do that with ArrayList, too. But then you need casts. Why not use List then... Looks much cleaner:
foreach (Register register in list)
{
Console.WriteLine("Name: " + register.name + " Surname: " + register.surname);
// or
// register.PrintData();
}
You're adding an instance of Register to your ArrayList. When you index into it, list[i], that returns this instance.
When you attempt to print this instance, you are in fact calling this method: Console.WriteLine(object), as it is the best fit. This method needs to get a string from your object, so it calls ToString() on it.
The default implementation of ToString() returns the namespace and type name for the current object. In this case, "WhateverYourBloodyNamespaceIs.Register".
Now you know you have an object of type Register, you can get name and surname from it, format them properly and write those to the console. You've got a PrintData method, use it.
ArrayList isn't generic, so you must cast from object back to Register. Or, be a big boy and use List<Register> instead of that 2002 ArrayList crap.
Whoever told you to use an ArrayList is playing tricks on you.
You need to pick up a copy of CLR Via C#. Skip the first two chapters. Start reading. Go. Run. I'm not joking.
Override ToString() method in your Register class, then you pass the 'temp' variable to Console.WriteLine() method.
I need some help with syntactic sugar.
I have a ThisClass[3] and ThatClass[3].
public class ThisClass
{
public string Thing1;
public string Thing2;
public string Thing3;
public string Thing4;
}
public class ThatClass
{
public string Thing1;
public string Thing2;
}
Each instance in the array of ThatClass was created based on an instance in the same position of array ThisClass.
So ThatClass[0] has its fields with the same values as ThisClass[0], except it only has 2 fields instead of 4.
I would like to now update each instance in the ThisClass array, with fields from the matching index position of the object in the ThatClass array. I could do nested for loops, but I need help thinking through a LINQ option.
ThisClass[0].Thing1 = ThatClass[0].Thing1;
ThisClass[0].Thing2 = ThatClass[0].Thing2;
works but I am sure could be done better. Using C#, .NET 4.5.
I don't see any need for nested loops:
for (int i = 0; i < theseClasses.Length; i++)
{
theseClasses[i].Thing1 = thoseClasses[i].Thing1;
theseClasses[i].Thing2 = thoseClasses[i].Thing2;
}
You could potentially add a CopyFrom(ThatClass) method to ThisClass, leading to:
for (int i = 0; i < theseClasses.Length; i++)
{
theseClasses[i].CopyFrom(thoseClasses[i]);
}
... but that's all I'd do. LINQ is do to with querying, not causing side-effects... I don't think it's a good fit here.
Attention: As #Jon put, LINQ is not about causing side-effects and if you do so you may end up with a code with unexpected behavior (but it's possible).
This code does that:
ThisClass[] these = new ThisClass[100];
ThatClass[] those = new ThatClass[100];
// init these and those items
those.Zip(these, (that, #this) =>
{
#this.Thing1 = that.Thing1;
#this.Thing2 = that.Thing2;
return that;
}).ToList();
As you're asking for LINQ... this will get you an unrelated IEnumerable<ThisClass>, and will not modify the original array.
(I'm assuming that the thisClass and thatClass arrays are called thisArray and thatArray, respectively)
thisArray.Select((n, x) => { n.Thing1 = thatArray[x].Thing1; n.Thing2 = thatArray[x].Thing2; return n; }).ToArray();
(If you really wanted LINQ and assigning it, just assign it back to the original array)
I have a City class and inside that a Detail class:
public class City {
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Notes { get; set; }
public class Detail
{
public Detail()
{
ImageFile = String.Empty;
Explanation = new HtmlText();
}
public string ImageFile { get; set; }
public HtmlText Explanation { get; set; }
}
}
In my code I have some lines that check how many details there are and if there are less than ten then I add new City.Details. I am using the code below to do this but it's located in a few different methods and does not look clean. Is there some way that I could simplify this and also add the logic of checking, counting and adding into my base City class?
foreach (int index in Enumerable.Range(0, 10 - vm.Details.Count()))
{
vm.Details.Add(new City.Detail());
}
You could add a MinReached and a FillDetails method to your City class.
The first one checks if you already reached the minimum, the second add new details up to ten.
public bool MinReached()
{
return this.Details.Count >= 10;
}
public void FillDetails()
{
for (int i = Details.Count; i <= 10; i++)
this.Add(new City.Detail());
}
If you always have the need to have to 10 Details to be made available can do something on these lines
Enumerable.Range(0, count).Select(i => new City.Detail()).ToList();
This is for 10 Details exclusively or if you want to add the remainders then calculate the difference then use .Concat() to append it on to the existing list.
As the others said, use something like the following
while(vm.Details.Count() < 10)
vm.Details.Add(new City.Detail());
or even a regular for construct
for(int x = vm.Details.Count(); x < 10; x++)
vm.Details.Add(new City.Detail());
Otherwise when other people read your code (or you look at it 3 months from now) the reaction is going to be "huh?" instead of just automatically recognizing what's happening.
Here are three ways to address the problem:
1) Just add ten Details to the City object when you create it, use those and then create more if/when necessary
2) Why do you need 10 Details if there aren't really 10 Details? As much as possible it's best to have your objects truly represent what they are... representing. So perhaps you are trying to fix what is just a symptom of a deeper problem. But if that's not the case, then...
3) As the others have mentioned just move this logic into your base class.
Edit: I should have also made clear on #3 that you need a way to make this process automatic so you don't have to explicitly call the procedure that pads it with extra Details. I don't have enough info from your code to know how exactly to tell you to do this but if you want to provide more info as to why it's important to have 10 Details then I'm sure I could help further.
Why can't you use for loop
for (int i = 0; i < 10 - vm.Details.Count(); i++) vm.Details.Add(new City.Detail());
I'm not sure what type your cm variable is but if you want to add something to your City class you could add a static method to the City class named something like SetDetailSize that accepts a collection of Detail objects and a size you want to force the collection to be and then just call City.SetDetailSize method passing in your collection.
"..do this using LINQ that would eliminate the need for the for loop altogether".
Surely you could do it with linq but that does not mean the looping would not take place. Use ForEach linq operator. BUt this code is similar to what you had written except its in linq style
LINQ Code
Enumerable.Range(0, 10-vm.Details.Count()).ToList().ForEach(counter=>vm.Details.Add(new City.Detail());
EDIT
Think this would solve your problem. As soon as your class instansiate , fill 10 cities by default. Use a counter for checking how many dummy elements are there in. Once user add a detail, replace the dummy with user's input. Put this in your base class and then you can forget about adding additional details at every other place
public class City
{
int counter = 0;
public City()
{
//fill 10 elements by default
Enumerable.Range(0, 10).ToList().ForEach(counter =>vm.Details.Add(new City.Detail());
}
.
.
}
//Now define your add method as following
public void AddDetails(Details d)
{
//remove the dummy element
vm.Details.RemoveAt(counter);
//add original element and increase the counter, so next element would be added at next index
vm.Details.insert(counter++, d);
}
Another option is to create an extension method for the List collection. This would allow you to call a method on a List of Detail objects from anywhere within your application. Do the following:
public static class ExtensionMethods
{
public static void SetDetailSize(this List<City.Detail> details, int size)
{
for (int i = 0; i < size - details.Count; i++)
details.Add(new City.Detail());
}
}
This solves the duplication of code problem because anywhere you have a List of City.Detail objects, you can just make the following call.
vm.Details.SetDetailSize(10);
Make sure you have a using statement that references the namespace for the ExtensionMethods class.
I'm very new with c#, and was previously attempting to ignore classes and build my small program structurally more similar to PHP. After reaching a road block, I'm trying to start over and approach the problem properly OO. I'm taking a long file, and in a loop, every time certain conditions are met, I want to make a new object. How can I have it create a new object, without having to specify a unique name?
Referral ObjectName = new Referral(string, string, int);
Secondly, once this is done, and the strings & int set their appropriate object properties, how can i unique-ify the class by one property, and then sort the class by another?
I'm sorry if these are basic questions, I have spent a large, large amount of time first trying to figure it out on my own with google, and a textbook. If only C# would allow multi-dimensional arrays with different types!
Thank you so much!
PS. I do mean to extract a list of unique objects.
All these answers, while helpful, seem to involve creating a shadow set of IEnumerables. Is there no way to do this with the class itself?
Trying the first solution, provided by Earwicker, adding each object to a List from within the loop, when I try to Write a property of the element to the console, i get the ClassName+Referral. What could I be doing wrong?--solved. still needed .property
still working. . .
C# does allow untyped arrays. All objects are derived ultimately from object, so you use an array or container of objects. But it's rarely necessary. How many types of object do you have?
Within the loop block, you can create an object exactly as you do in that line of code (except with the syntax fixed), and it will be a new object each time around the loop. To keep all the objects available outside the loop, you would add it to a container:
List<Referral> referrals = new List<Referral>();
// in the loop:
Referral r = new Referral(str1, str2, num1);
referrals.Add(r);
Suppose Referral has a numeric property called Cost.
referrals.Sort((l, r) => l.Cost - r.Cost);
That sorts by the cost.
For ensuring uniqueness by some key, you may find it easier to pick a more suitable container.
Dictionary<string, Referral> referrals = new List<Referral>();
// in the loop:
Referral r = new Referral(str1, str2, num1);
referrals[str1] = r;
This stores the referral in a "slot" named after the value of str1. Duplicates will overwrite each other silently.
First, you're going to need to spend some time familiarizing yourself with the basics of the language to be productive. I recommend you take a little time to read up on C# before getting in too deep - otherwise you'll spend a lot of your time spinning your wheels - or reinventing them :)
But here's some info to get you started.
Typically, in C# you create classes to represent elements of your program - including those that are used to represent information (data) that your program intends to manipulate. You should really consider using one, as it will make data manipulation clearer and more manageable. I would advise avoiding untyped, multi-dimensions array structures as some may suggest, as these rapidly become very difficult to work with.
You can easily create a Referall class in C# using automatic properties and a simple constructor:
public class Referall
{
// these should be named in line with what they represent...
public string FirstString { get; set; }
public string AnotherString { get; set; }
public int SomeValue { get; set; }
public Referall( string first, string another, int value )
{
FirstString = first;
AnotherString = another;
SomeValue = value;
}
}
You can add these to a dictionary as you create them - the dictionary can be keyed by which ever property is unique. Dictionaries allow you to store objects based on a unique key:
Dictionary<string,Referall> dict = new Dictionary<string,Referall>();
As you process items, you can add them to the dictionary:
Referall ref = new Referall( v1, v2, v3 );
// add to the dictionary, keying on FirstString...
dict.Add( ref.FirstString, ref );
If you need to sort items in the dictionary when you're done, you can use LINQ in C# 3.0:
IEnumerable<Referall> sortedResults =
dict.Values.OrderBy( x => x.AnotherString );
You can sort by multiple dimension using ThenBy() as well:
IEnumerable<Referall> sortedResults =
dict.Values.OrderBy( x => x.AnotherString )
.ThenBy( x => x.SomeValue );
List<Referral> referrals = new List<Referral>();
for (...)
{
referrals.Add(new Referral(string1, string2, number1));
}
Then, if you're using Linq (which I highly suggest), you can do this:
IEnumerable<Referral> sorted = referrals.OrderBy(x => x.string1).ThenBy(x => x.string2);
Otherwise, you can use the Sort() method on List<Referral>.
You can create an object without a reference, but you won't have any access to it later:
new Referral(string, string, int);
If you wish to put them in an array/list, these different types need to have a common base class. This is called polimorfism, which is a very important concept in OO programming.
You cannot ignore classes while using c#. Don't resist the change!
Do you really not need to create a class here? Do you really not need to give it a name? C# does allow loose typing, but type safety is a good thing.
I don't fully understand what you're trying to do. But maybe LINQ is what you're looking for. There's tons of documentation around, but as a quick 'teaser' have a look at the 101 Linq samples on MSDN
C# includes a wonderful feature called "iterator blocks". What you want to do is use the yield keyword to create an Enumerable of your Referal object, something like this (not that I'm making the file format and property names up, because you didn't share that):
public class Referral
{
public Guid id { get; private set; } // "uniquify"
public int ReferringId { get; set; }
public string ReferrerText { get; set; }
public string ReferrerDescription { get; set; }
private Referral()
{
id = new Guid();
}
private Referral(string Text, string Description, int ReferringId) : this()
{
this.ReferrerText = Text;
this.ReferrerDescription = Description;
this.ReferringId = ReferringId;
}
public static IEnumerable<Referral> GetReferrals(string fileName)
{
using (var rdr = new StreamReader(fileName))
{
var next = new Referrer();
int state = 0;
string line;
while ( (line = rdr.ReadLine() ) != null)
{
switch (state)
{
case 0:
next.ReferrerText = line;
state = 1;
break;
case 1:
next.ReferrerDescription = line;
state = 2;
break;
case 2:
next.ReferringId = int.Parse(line);
yield return next;
next = new Referral();
state = 0;
break;
}
}
}
}
}
Now you want to sort the referrals and presumable enumerate over them for some purpose. You can do that easily like this:
foreach (var referral in Referral.GetReferrals(#"C:\referralfile.txt").OrderBy( r => r.Text ) )
{
OutputReferral(referral);
}