Basic C# lists - Adding an unknown number of items - c#

I am teaching myself C# and modern Windows programming in general and had a question about lists in C# that I haven't found an answer for. The reference book I am using seems to indicate that in C# before you can assign a variable to a list, you need to define a new variable for each item in the list and assign it's value.
I am trying to write a simple program that will let me write text notes, assign keywords to them, and them filter the resulting group of notes based on the keywords I select. From what I can figure out, a List seems the best mechanism in C# to do this since there will be an unknown number of notes. However, I am struggling with the idea of defining a unknown number of variable names ahead of time to store the notes in, to add to the List.
Am I looking at the wrong construct in C# for this, misunderstanding what is required to assign a variable value to a List, or lacking knowledge of some mechanism for auto-creating variables and variable names that can be leveraged as part of a list? Something else?
Sorry if this is too simple a question for SE crowd, but this seemed like the best place to ask.

A List<T> is a data structure that lets you store an unknown number of T, but you don't need variable names for the elements of the list - just for the list itself.
Is this what you need?
var notes = new List<string>()
{
"This is a note.",
"I am happy",
};
notes.Add("This is another happy note");
notes.AddRange(new [] { "Another happy notes", "This is also a note", });
var selected = notes.Where(n => n.Contains("happy")).ToList();
The values in selected are:
"I am happy", "This is another happy note", "Another happy notes"

I am not sure what exactly is your confusion. But it sounds like you are having issues in constructing class or using C# types. To keep it simple for now you can follow something in this manner.
// This represents individual note
public class Note
{
// Initialize keywords list in constructor
// in order to avoid Null reference exception.
public Note() {
Keywords = new List<string>();
}
public string Title { get; set; }
public string Content { get; set; }
public List<string> Keywords { get; set; }
}
// In main code, you can simply have List<Note> to hold collection of any no of notes.
// Also, when user adds a note you will create a new Note instance and add to collection.
List<Note> notes = new List<Note>();
Note newNote = new Note();
newNote.Title = "Note 1";
newNote.Content = "Note 1 Content";
newNote.Keywords.Add("Test1");
notes.Add(newNote);

Related

Should this list initializer behavior be reported as bug in the Visual Studio C# Compiler?

First of all I will say that I've changed my design and no longer need that but getting a good answer for that will still be nice
I have the following class, ListContainer, in my code (The attached codes are all mcve):
class ListContainer
{
public object ContainedList
{
get; private set;
}
public int Value
{
get; private set;
}
public ListContainer(object list, int value)
{
ContainedList = list;
Value = value;
}
}
And in some other class in my code I have a List<ListContainer> and I need each ListContainer to contain this List<ListContainer>, so I can implement it like that:
//Field in the class
List<ListContainer> mContainers = null;
//In the constructor:
mContainers = new List<ListContainer>();
mContainers.Add(new ListContainer(mContainers, SOME_CONST));
mContainers.Add(new ListContainer(mContainers, SOME_OTHER_CONST));
Than it works fine, but when I've tried to use list initializer:
//Field in the class
List<ListContainer> mContainers = null;
//In the constructor:
mContainers = new List<ListContainer>
{
new ListContainer(mContainers, SOME_CONST),
new ListContainer(mContainers, SOME_OTHER_CONST)
}
You would expect the results to be equivalent but in reality the result looks like that:
mContainers
[0] - ListContainer
ContainedList = null
Value = SOME_CONST
[1] - ListContainer
ContainedList = null
Value = SOME_OTHER_CONST
Seeing this results I've inspected the output MSIL of this C# compilation and seen the following code:
Now, this explains why the problem occurs, and I've even checked out in the CSharp Language Specification document and this is the defined behavior:
A List can be created and initialized as follows:
var contacts = new List<Contact> {
new Contact {
Name = "Chris Smith",
PhoneNumbers = { "206-555-0101", "425-882-8080" }
},
new Contact {
Name = "Bob Harris",
PhoneNumbers = { "650-555-0199" }
}
};
which has the same effect as
var __clist = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
__clist.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
__clist.Add(__c2);
var contacts = __clist;
where __clist, __c1 and __c2 are temporary variables that are otherwise invisible and inaccessible.
So obviously this behaviour is intended. Is there a good reason everything is done on the temporary variable and not on the original one? since it seems like a wrong behaviour to me.
The reason is to avoid race conditions with concurrent threads accessing the original variable where you would add your elements. An inconsistency would appear if a thread accesses the variable while not all elements are added to it yet.
Two threads accessing the same variable would therefore get an inconsistent list, with different elements in it.
This would not come as a shock if the elements are added on different lines, but since you use an object initializer, it is normal to perceive the object as directly initialized with all its elements in it, hence the need of a temporary, invisible, variable.
Is there a good reason everything is done on the temporary List and not on the original one?
There is no original list:
var __clist = new List<Contact>();
// …
__clist.Add(__c1);
// …
__clist.Add(__c2);
var contacts = __clist;
Only one list is ever created. What you probably mean is that it’s done on a temporary variable instead of the original variable, but that has no practical effect—other than probably being easier to implement. This is especially true if you think about that collection initialization is not limited to the context of variable assignments:
SomeMethodCall(new List<int>() { 1, 2, 3 });
Since there is no reference to that list, the simplest solution to implement this is just to use a temporary variable that holds the list, and put that variable at the place of the initializer then.
What’s also important about this is that the old value is completely overwritten. So in your mContainers = new List<ListContainer>, the old value of mContainers is never being looked at for the purpose of the initializer syntax.
It’s probably a good idea to think about the collection initialization as an “atomic” operation. The list only exists (at least to you) once the whole initializer completes. So you cannot reference itself from within the initializer.
Assignments are first evaluated on the right side of the = and then the assignment takes place. So mContainers is still null.

List.First not returning anything, while there is something

I'm making something like an objective list with C#.
I got it working and all, able to draw the objective list inside Unity, and make new objectives with them. I had an idea on how to delete something, I used System.Linq and the List.First function to search for it. But it does not seem to work.
This is the 'Finishing and Creating Objectives' code.
public void NewObjective(string objectiveText, string objName)
{
objectives.Add(new Objective(objectiveText, objName)); //Add to the list Objective.
middleText("New Objective: " + objectiveText); //Display text on screen.
}
public void FinishObjective(string shortObj)
{
var value = objectives.First(x => x.ObjectiveName.Contains( shortObj ));
string completedTask = value.objectiveDescription;
middleText("Completed Objective: " + completedTask); //Display text on screen.
objectives.Remove(value); //Remove value. (Which doesn't find anything for some reason, so it can't delete anything from the list.)
}
And then in another class, I have it make a new objective, like this.
GameController.gameController
.GetComponent<GameController>()
.NewObjective("Foobar.", "foo"); //First is the Quest description,
and the second is the name for easy deletion.
(I've also included a objectiveID method but I've omitted for ease.)
In that same class, when the player completes something, I have this.
GameController.gameController
.GetComponent<GameController>()
.FinishObjective("foo"); //This has two possible methods,
the object ID (if defined) or the name of the objective.
What is going on, what am I doing wrong and what can I do to fix this?
Thanks for the help.
Edit:
There is no actual error. It's just that it doesn't find anything, while there is something. Objectives is just easily defined as List objectives = new List(); inside the class.
This is the objectives class:
public class Objective : MonoBehaviour
{
public string objectiveDescription;
public string ObjectiveName;
public int ObjectiveID;
public Objective(string objective, string objectiveName)
{
objectiveDescription = objective;
ObjectiveName = objectiveName;
}
public Objective(string objective, string objectiveName, int objectiveID)
{
objectiveDescription = objective;
ObjectiveName = objectiveName;
ObjectiveID = objectiveID;
}
}
You may need to implement something like IEquatable inside your Objective class. You are asking the computer to check if ObjectiveName contains shortObj but it does not know how to make this comparison. is ObjectiveName a string? List of strings? a(n) Objective class? If it's a list of strings that we are calling "objective names" then the expected element would be a string (something like shortObj.ObjectiveName assuming that's a string). If ObjectiveName is a list of Objective classes and you are asking if this list contains a specific Objective element called shortObj, then you'll need to implement IEquatable into your Objective class.
EDIT
In light of recent comments, try something like:
var value = objectives.AsEnumerable().Where(x => x.ObjectiveName == shortObj).FirstOrDefault();

Store anonymous values in a list and extract each individually

I'm trying to store information in a block of anonymous values
//holds all info
var jobs = new { newJob, numBytes, requiredTime };
then take that information and place it into a list as a single element
//puts above info into a list
joblist.Add(Convert.ToString(jobs));
Console.WriteLine(joblist[0]); //testing purposes
now what I would like to do is be able to call joblist and take the value of example numBytes at position 4.
Is this possible? Or could someone help with an alternate way of doing this? Much thanks!
Create a named class. Then you can have a list of objects of that type and manipulate that list in any way you want.
Using classes is best-practice for what you are trying to do. By default you should consider storing structured data in an object model consisting of custom classes. There is another answer here which is proposing to use dynamic - this is valid and has its place, but it is more of a last resort solution. What you want is to play to the strength of C# which are rich classes and static typing. Anonymous types are also statically typed, but as you cannot name the type you cannot declare a statically typed list to hold them. You also can't use them as return types of methods.
The "normal" thing to do in C# would be to create a class to hold the information that you want to store. For example:
public class Job
{
public string Name { get; set; }
public int NumBytes { get; set; }
public DateTime RequiredTime { get; set; }
}
Then you can add these to a list:
var jobs = new List<Job>();
var aJob = new Job();
aJob.Name = "Job 1";
aJob.NumBytes = 123;
jobs.add(aJob);
Then you can access jobs by its index in the list:
var jobNumBytes = jobs[3].NumBytes;
One thing to note about C#, when you do:
new { newJob, numBytes, requiredTime };
The compiler, at build time, just creates you a strongly typed class (just like the Job class I created above) and generates a random name for it. It infers the property names and types from the variables that you are assigning to it. The created .exe or .dll actually does contain a class definition for this type, you just can't easily get to it from other places in your code. It isn't truly "dynamic". So using that syntax is usually just a lazy way of declaring a class that you just need for a moment. Usually just inside 1 method, then you don't need it any more. Creating a named class is usually what you want to do.
Actually I don't know exactly what you mean with "now what I would like to do is be able to call joblist and remove for example numBytes at position 4."
But I guess you just want to put the objects in a list and query for numBytes and maybe remove some elements.
With dynamics you can handle dynamic objects...
var jobs = new List<dynamic>();
for (int i = 0; i < 100; i++)
{
string newJob = "Job" + i;
int numBytes = i;
TimeSpan requiredTime = new TimeSpan(0,0,i);
//holds all info
var job = new { newJob, numBytes, requiredTime };
jobs.Add(job);
}
jobs.RemoveAll(p => p.numBytes > 50);
Instead of this, I agree with the comments below your question and would create a normal class which holds the properties you need and simply put instances of that into a list. Dynamics should be used only in very rare situations, and yours doesn't sound like it is extremely special.

Add property to a list?

I have a list with a lot of objects List<MyObjects> - Im iterating through this list and reading the objects. Thats all fine. I just relized now, what it would be awesome if I could add 1 more special value to this List - not an Object, just 1 value of something (a string).
I could now create a class like
class Needless
{
public List<MyObjects> MyList { get; set; }
public string MyValue { get; set; }
}
but Im trying to avoid that. I just really need 1 string with an input every List<MyObjects> Any ideas?
Tuple<string, List<MyObject>> is an option. However, if you are going to use this pairing a lot, I would advise creating a custom class for it to be more explicit - either like you have done, or by deriving List<MyObject> and adding the string as a property.
If you are working "in scope" you could always make anonymous types:
var t = new { TheString = "", TheList = new List<MyObject>() };
var list = t.TheList;
var s = t.TheString;
But this only really has benefit in the scope of a method. The compiler can give IntelliSense for this and it is strongly-typed at compile time.
Or if you really want to go all out, use ExpandoObject from System.Dynamic:
var expando = new ExpandoObject();
expando.List = new List<MyObject>();
expando.TheString = "";
This is typed in-line without any IntelliSense support, and will invoke the DLR. ExpandoObject simply uses an IDictionary<string, object> under the hood, so...
var dict = (IDictionary<string, object>)expando;
...is valid.
The last option is a little tongue-in-cheek. It'll work, but the development experience against it isn't ideal compared to other options. That said, we use ExpandoObject in our test code, but I can't remember why. One can only hope the reasoning was sound, but it was likely a developer getting giddy with new toys...
you can inherit the List and simply add your property, it's a bit cleaner than composite the List inside another class
You could extend a List implementation with your Needless class. That way you can still treat your list as a list.

c# looping object creation

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

Categories