Class with multiple objects or list - c#

I am new to OOP and C# so have what is potentially a noob question.
Looking at classes all the code I see requires the object names to be hardcoded. Say for example we don't know how many Customers a user will enter and what their names are. So how does that work with creating instances of a class?
Is it the case that you would only create one class of customer as a data type (with methods as required) and store them in say a list? Or can a class hold multiple instances of an object but create them and name them dynamically in relation to user input?
I've not quite got my head around how a class works with holding records in memory etc.

If you will iterate throught the objects in some moment I'd use a List.
As mentioned by #wazz you should store them in a list, like so:
List<Customer> customers = new List<Customer>();
Then you can start working with the list, adding new instances, removing etc.
You can check more information at the docs.

Note that a class is a type. You can think of it as template used to create objects (also called instances of this class). So, you can have many different objects of the same class.
You can differentiate between different objects of the same class by assigning them to different variables:
Customer a = new Customer { Name = "customer a" };
Customer b = new Customer { Name = "customer b" };
This has its limits, of course, as you do not want to declare one thousand variables when you have one thousand customers!
Here, collections come into play.
Store the customers in a collection. There are many different collection types. Probably the 2 most popular are List<T> and Dictionary<TKey, TValue>.
List<T> where T is a type (Customer in your case):
List<Customer> customers = new List<Customer>();
Customer c = new Customer { Name = "My first customer" };
customers.Add(c);
// You can re-use the same variable for another customer.
c = new Customer { Name = "My second customer" };
customers.Add(c);
// Or skip the temporary variable alltoghther.
customers.Add(new Customer { Name = "My third customer" });
The customer in the list can be accessed through a zero-based index
Customer c = list[5]; // Get the 6th customer
Lists can also be enumerated:
foreach (Customer cust in customers) {
Console.WriteLine(cust.Name);
}
Dictionary<TKey, TValue>: dictionaries allow you to lookup a customer by a key, e.g. a customer number.
Dictionary<string, Customer> customers = new Dictionary<string, Customer>();
Customer c = new Customer { Id = "C1", Name = "My first customer" };
customers.Add(c.Id, c); // The Id is used as key, the customer object as value.
Then you can get it back with
Customer c = customers["C1"];
This throws an exception if the supplied key is not existing. To avoid an exception you can write:
string id = "XX";
if (customers.TryGetValue(id, out Customer cust)) {
Console.WriteLine(cust.Name);
} else {
Console.WriteLine($"Customer '{id}' not found");
}

To help you understand a class first.
A class is a blueprint of what the data will look like. If you were to build, a house for example, you would first make the blueprints. Those prints could be used to make many houses that look that same. The differences in those houses vary in areas such as color, wood, brick, or vinyl, and if is has carpet or not. This is just an example but let's make this house into a class (blueprint) and then let's make several of those houses into actual individual objects; finally let's reference all of those in the same location (using a list).
First let's decide what our house options are, the things that set them apart. It's color, exterior, and with or without carpet. Here we will make color a string (text) for simplicity, the exterior as an enum ( so the options are hard coded), and the carpet or not a bool (true or false).
First let's make the enum since it is separate than a class but used within a class.
public enum Exterior { Brick, Vinyl, Wood }
Now let's make the class (the blueprint).
public class House
{
public string Color { get; set; }
public Exterior Exterior { get; set; }
public bool HasCarpet { get; set; }
}
Now that we have the blue print let's actually make some houses. When we make them we need to have a way to locate them in memory so we assign them to a variable. Let's pretend we are in a console app and this is the first few lines of the main method.
var house1 = new House();
var house2 = new House();
var house3 = new House();
Now we have 3 individual houses in memory and can get to any of them by referencing house1, house2, or house3. However, these houses are still built identical and unfortunately have no color (since the string Color is empty), are default to Brick (since the first enum is Brick), and have no carpet (since HasCarpet defaults to false).
We can fix this by referencing each house object reference and assigning these values like so...
house1.Color = "Red";
house1.Exterior = Exterior.Wood;
We could have given the classes a constructor that required these values as parameters to start with or we can do it a simpler way inline (thanks to the power of C# syntax).
var house1 = new House()
{
Color = "Red",
Exterior = Exterior.Wood
};
We could also give it carpet but since this house isn't going to have any and it defaults to false I've left it out.
Ok, so let's say we have all 3 houses built via our blueprint. Let's now store them together in a List. First we need to make the List into an object and reference that also.
var houses = new List<House>();
Now let's add the houses to the list.
houses.Add(house1);
houses.Add(house2);
houses.Add(house3);
Now houses is a reference to all of our house objects. If we want to get to a house in the list we could use an index (starting at 0) and get that location in the list. So let's say that house2 needs to have carpet but we want to use the list now to reference it. Note: There are quite a few ways to reference the items in a list, this one is elementary.
houses[1].HasCarpet = true;
I did this on my phone, hopefully there are no errors. My intentions are to clearly answer your question and educate and help you better understand classes.

Most likely, you would use an IEnumerable<Customer>. A Customer class in real life is related with a database.
When dealing with databases, Linq comes to mind which would use an IQueryable<Customer> (inherited from IEnumerable). As need arises you would also use other collection types like List<Customer>, Dictionary<...> etc

Related

Add object to ICollection and then Add ICollection to a List<> ASP.NET

I'm not sure if this is possible or even makes total sense, but I need to add an object let's call it Person to an ICollection List People and add the collection to a List<> , in order to have the collection of people in a List<> that also will contain other parameters.
I am not sure how to do this but I can show you what I have sketched so far.
public void addPeopleToList(string PersonId)
{
Person p = findPerson(PersonId); /*Method that takes the ID and
returns an object from another List*/
ICollection<People> ICollectionPeople; //Create the ICollection
ICollectionPeople.Add(p); //Add Person to Collection
List.Add(ICollectionPeople); //Add Collection to List
}
If this way is not the proper way to do it, I am open to all other suggestions.
It's probably easier if you simply told us what exactly you want to achieve, but anyway:
You need to assign a value to ICollectionPeople (also maybe it's worth renaming it and following the naming conventions). Maybe
ICollection<People> peopleCollection = new List<People>();
Though do you really need that explicit type? You could use var.
You need to create an instance of the list you want to add your collection of people to. Maybe
var list = new List<People>();
Then finally, use AddRange() like
list.AddRange(peopleCollection);
I think what you need is another class.. like...
public class PersonWithAttributes : Person {
// add attribute properties here
}
Then in your code above you would change List to be of type List<PersonWithAttributes> and instead of .Adding the collection you would call List.AddRange(ICollectionPeople). After which you would need to loop over the List items and add the extra attributes you were talking about.
Not sure but looks like you wanted to have a List<List<People>> .. if that's so then your code is missing initialization of the collection. You need to change a bit
public void addPeopleToList(string PersonId)
{
Person p = findPerson(PersonId); /*Method that takes the ID and
returns an object from another List*/
List<Person> ICollectionPeople = new List<Person>(); //Create the
ICollectionPeople.Add(p); //Add Person to Collection
List<List<Person>> personLists = new List<List<Person>>()
personLists.Add(ICollectionPeople); //Add Collection to List
}

Declare Instance of Class with Dynamic Name

So I have created a Class called SnakeItem, I want to create a new instance of the snake item when the snake grows, they follow a naming convention of snake_Piece_[NUMBER]. There is already snake_Piece_0 and I want to declare a new instance in the code. I'm not sure how to put it...
SnakeItem snake_Piece_0;
public game_Window()
{
InitializeComponent();
snake_Piece_0 = new SnakeItem(Resource1.Black, 0, 0);
}
Then in this function, I want to create it. (after snake_length++:) I need to name to increment so it follows the snake_length Variable. i.e. if snake_length = 1 then it will create a piece with the name snake_Piece_1.
private void fruit_Collision()
{
if (snake_Piece_0.Top == Fruit_Item.Top && snake_Piece_0.Left == Fruit_Item.Left)
{
snake_Length++;
}
}
I'm not sure what I can say if it's not possible I would have to declare all 400 snake pieces beforehand which is what I'm trying to avoid.
This is a common thing that beginners do - trying to dynamically create new variables with new names. I tried to do this too, until I learned about Lists.
Lists are objects that can store a variable number of objects. Here is how you can create an empty list of SnakeItem:
List<SnakeItem> snakeItems = new List<SnakeItem>();
You can add, access, and delete items in the list. Here are some examples:
snakeItems.Add(new SnakeItem(Resource1.Black, 0, 0)); // add a new item to the list
snakeItems[0] // access the first item in the list. 0 is the first, 1 is the second, etc.
snakeItems.RemoveAt(0) // remove the first item of the list
Basically, whenever you want to create a new "variable name", just Add a new item to the list:
if (snakeItems[0].Top == Fruit_Item.Top && snakeItems[0].Left == Fruit_Item.Left)
{
snakeItems.Add(...);
}
As you can see, snake_Length is not needed any more! You can access how many snake items there are using .Count. Replace all the snake_Length with snakeItems.Count.
Learn more about lists here.

C# - Getting the 3 lowest values in a List, measured by a public property of the objects within the List

Sorry if my terminology is not great, I'm not a professional programmer.
I have a List< Something >, whereby the 'Something' is a struct. This struct contains objects, which each have their own public properties/fields in the classes. I want to sort the list in order - but by the values found in these nested properties/fields. Then I want to return a list of these values, not the structs.
I know this is very confusing but I've had trouble trying to do it. At the moment I just get a list returned with a count of 20 (which is the full data set I'm using), but I want the 3 values only with the smallest value.
For context and further explanation, here is some code I'm using:
// Returns 3 nearest stations to the location specified
public static List<TrainStation> nearbyStations(GeoCoordinate location)
{
List<StationWalk> stations = new List<StationWalk>();
foreach (TrainStation s in All)
{
stations.Add(new StationWalk(s, new Walk(location, s.location)));
}
// return 3 TrainStation objects that have the lowest StationWalk.Walk.duration values corresponding with them in the StationWalk struct
stations.OrderBy(walks => walks.walk.duration).Take(3);
List<TrainStation> returnList = new List<TrainStation>();
foreach (StationWalk s in stations)
{
returnList.Add(s.station);
}
return returnList;
}
private struct StationWalk
{
public StationWalk(TrainStation station, Walk walk)
{
this.station = station;
this.walk = walk;
}
public TrainStation station;
public Walk walk;
}
'Walk' is a class that contains a 'duration' field. This represents the time it takes to walk. More specifically, my overall goal here is to figure out which 3 walks are the fastest walks out of all 20 in the list. But the 'walks' are properties of the StationWalk struct, and the 'duration' is a property of the Walk.
How would I go about doing this? Really sorry if this isn't well explained, it's confusing to myself despite writing it myself, yet alone trying to explain it to others.Appreciate any help.
The OrderBy and Take both return a new collection, they do not modify the existing collection, so you would need to store the reference to new collection returned by the methods like:
stations = stations.OrderBy(walks => walks.walk.duration).Take(3).ToList();
and if you want to keep reference to the original list for further usage down in your code, then just store the result in a local variable:
var lowestThreeStations = stations.OrderBy(walks => walks.walk.duration).Take(3).ToList();

Linq progressive state based query

Firstly apologies for the poor title. Absolutely no idea how to describe this question!
I have a "Relationship" entity that defines a relationship between 2 users.
public class Relationship{
User User1{get;set;}
User User2{get;set;}
DateTime StateChangeDate {get;set;}
//RelationshipState is an Enum with int values
State RelationshipState State{get;set;}
}
Relationship state example.
public enum RelationshipState{
state1 = 1,
state2 = 2,
state3 = 3,
state4 = 4
}
A Relationship entity is created each time the RelationshipState changes. So for any pair of users, there will be many Relationship objects. With the most recent being current.
I'm trying to query for any Relationship object that represents a REDUCTION in RelationshipState for a particular pair of users.
So all the RelationshipObjects for all the users. That have a later Date than one with a higher RelationshipState.
I'm finding it very hard to figure out how to accomplish this without iterating over the entire Relationship table.
First, create a query to return all the combinations of users and a child that lists all the status changes. For more information, google LINQ Group By.
Then using your collection, filter out all the ones you don't want by looking at the last two status changes and seeing if it's gone down.
Here's an example, tested in LinqPad as a C# Program:
public enum RelationshipState {
state1 = 1,
state2 = 2,
state3 = 3,
state4 = 4
}
public class User {
public int id {get;set;}
}
public class Relationship{
public User User1{get;set;}
public User User2{get;set;}
public DateTime StateChangeDate {get;set;}
//RelationshipState is an Enum with int values
public RelationshipState State {get;set;}
}
void Main()
{
var rs=new List<Relationship>() {
new Relationship{ User1=new User{id=1},User2=new User{id=2},StateChangeDate=DateTime.Parse("1/1/2013"),State=RelationshipState.state2},
new Relationship{ User1=new User{id=1},User2=new User{id=2},StateChangeDate=DateTime.Parse("1/2/2013"),State=RelationshipState.state3},
new Relationship{ User1=new User{id=1},User2=new User{id=3},StateChangeDate=DateTime.Parse("1/1/2013"),State=RelationshipState.state2},
new Relationship{ User1=new User{id=1},User2=new User{id=3},StateChangeDate=DateTime.Parse("1/2/2013"),State=RelationshipState.state1},
new Relationship{ User1=new User{id=2},User2=new User{id=3},StateChangeDate=DateTime.Parse("1/2/3013"),State=RelationshipState.state1}
};
var result=rs.GroupBy(cm=>new {id1=cm.User1.id,id2=cm.User2.id},(key,group)=>new {Key1=key,Group1=group.OrderByDescending(g=>g.StateChangeDate)})
.Where(r=>r.Group1.Count()>1) // Remove Entries with only 1 status
//.ToList() // This might be needed for Linq-to-Entities
.Where(r=>r.Group1.First().State<r.Group1.Skip(1).First().State) // Only keep relationships where the state has gone done
.Select(r=>r.Group1.First()) //Turn this back into Relationship objects
;
// Use this instead if you want to know if state ever had a higher state than it is currently
// var result=rs.GroupBy(cm=>new {id1=cm.User1.id,id2=cm.User2.id},(key,group)=>new {Key1=key,Group1=group.OrderByDescending(g=>g.StateChangeDate)})
// .Where(r=>r.Group1.First().State<r.Group1.Max(g=>g.State))
// .Select(r=>r.Group1.First())
// ;
result.Dump();
}
Create a stored procedure in the database that can use a cursor to iterate the items and pair them off with the item before them (and then filter to decreasing state.)
Barring that, you can perform an inner query that finds the previous value for each item:
from item in table
let previous =
(from innerItem in table
where previous.date < item.Date
select innerItem)
.Max(inner => inner.Date)
where previous.State > item.State
select item
As inefficient as that seems, It might be worth a try. Perhaps, with the proper indexes, and a good query optimizer (and a sufficiently small set of data) it won't be that bad. If it's unacceptably slow, then trying out a stored proc with a cursor is most likely going to be the best.

Is a Collection of Collections possible and/or the best way? C# .Net 3.5

I have created a class for a dashboard item which will hold information such as placement on the dashboard, description, etc. I am currently using a pair of Collections to hold those dashboard items contained in the "library" and those items showing on the dashboard itself. I have been asked to make this dashboard multi-tab, and my first inclination was to make a new Collection for each tab. For this I would want some type of array or collection which could have many of these dashboard item collections added to it as more tabs are added to the dashboard.
Is this possible, and if so, could I get a little code snip for the declaration of such a collection? I have considered using a single collection with a variable to show which tab the item will be shown in... However, the display and routines to manage dashboard item movement between screen and library currently need those individual collections.
Edit: Thank you for your answers. While I do find them all interesting I believe I am going to go with James solution and will be marking it as the accepted answer.
List< List<Placement>> ListofListOfPlacements = new List< List<Placement>> ();
List<Placement> dashboard1 = new List<Placement>();
List<Placement> dashboard2 = new List<Placement>();
List<Placement> dashboard3 = new List<Placement>();
List<Placement> dashboard4 = new List<Placement>();
ListofListOfPlacements.Add(dashboard1);
ListofListOfPlacements.Add(dashboard2);
ListofListOfPlacements.Add(dashboard3);
ListofListOfPlacements.Add(dashboard4);
Since you are talking about tabs, it sounds to me like you want something closer to a dictionary keyed on the tab name, with a set of items per tab. .NET 3.5 added the ILookup<,> interface:
ILookup<string, Foo> items = null; //TODO
foreach (Foo foo in items["SomeTab"])
{
Console.WriteLine(foo.Bar);
}
Note that the MS implementation is immutable - you can't edit it after creation; however, I wrote an EditableLookup<,> in MiscUtil that allows you to work more effectively (just like a regular .NET collection):
var items = new EditableLookup<string, Foo>();
items.Add("SomeTab", new Foo { Bar = "abc" });
items.Add("AnotherTab", new Foo { Bar = "def" });
items.Add("SomeTab", new Foo { Bar = "ghi" });
foreach (Foo foo in items["SomeTab"])
{ // prints "abc" and "ghi"
Console.WriteLine(foo.Bar);
}
Without EditableLookup<,>, you need to build the lookup via the Enumerable.ToLookup extension method.
If any part of this sounds like an option, I can add more detail...
Why not create a class that contains your second collection and any of the previous information, and just have a collection of these items?
why not put your collection of dash board items in a tabpage class?
like so:
public class DashboardTabPage : TabPage
{
public List<DashboardItem> DashboardItems { get; set; }
public DashboardTabPage() : this (new List<DashboardItem>())
{
}
public DashboardTabPage(List<DashboardItem> items) :base ("Dashboard Thing")
{
DashboardItems = items;
}
}
public class DashboardItem { }
then you can do this:
c.TabPages.Add(new DashboardTabPage());
I think you should go with composition, as below
Per user Dashboard-home(having multiple tabs) object containing list of dashboard objects containing list of dashboard item objects having various operations on them defined. Again the dashboard item can be a usercontrol having all possible events defined which are handled either there in dashboard item object and/or left to do for dashboard UI object that'll work with individual dashboard objects.
I guess this would be better manageable, if you are going to follow SOA and reusable component based architecture.
You can also use a tuple like this: List<(string Father, IEnumerable<string> Children)>.
Example:
var list = new List<(string Parent, IEnumerable<string> Children)>
{
("Bob", new[] { "Charlie", "Marie" }),
("Robert", new[] { "John", "Geoff", "Oliver" }),
};

Categories

Resources