I have a List called
private List<Car> Cars; //Car is another class
I want to create a new list from the information in the List< Car > Cars that uses parameters to specify a year range to extracted from the Cars list and then placed into a new list called
private List<Car> getCars;
The code is as follows. Please note it is only part of a project so not all code is provided.
private List<Car> Cars;
private List<Car> getCars;
public List<Car> GetCars(int fromYear, int toYear)
{
getCars = new List<Car> { };
foreach (Car c in Cars)
if (c.Year >= fromYear && c.Year <= toYear)
getCars.Add(c);
return getCars;
}
The problem I'm having is although there are no errors showing up when I run the code the new list does not print out, instead it print's out
System.Collection.Generic.List'1[Lab__2.Car]
Any help would be great in how to make it print out the list's objects instead of what is above. Finally My lecturer has specified that he wants the method formatted as such
public List<Car> GetPrices(int year)
{
}
What you're seeing is the output you get when you call print directly on a list. It won't automatically print the contents, you must print each item yourself.
You are surely calling the ToString() method directly on the List but it will only print its type:
Default implementations of the Object.ToString method return the fully qualified name of the object's type. - MSDN
So, you must iterate through the items in the list and print it's details. Example:
foreach (Car c in Cars) {
Console.WriteLine(c.Name); //I do not know what properties you have in the class Car. Change accordingly.
}
Or you can use String.Join():
String.Join(Environment.NewLine, Cars); //You can change 'Environment.NewLine' to ", " if you want a comma instead of a new line.
You can print the items of the list by iterating over them, and printing each object individually:
for (Car car in Cars)
{
Console.WriteLine(car.ToString);
}
Doing this Console.WriteLine(Cars) will only give you information about the List object, usually being the full type and maybe the address in memory, depending on the runtime.
If you want to print the items of the list after getting them from a method, do this:
for (Car car in GetCars(fromYear, toYear))
{
Console.WriteLine(car.ToString);
}
When printing an object that is not a string (or other character sequence) you might want to override the ToString method that is inherited from Object in order to specify the information of the object (or Car in this case) you want to print.
Try something along these lines:
public Class Car
{ // guessing here
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public override string TosString()
{
return "Make: " + Make + ", Model: " + Model + ", Year: " + year;
}
And then, somewhere in your program:
foreach(var car in CarList.Where(c => c.Year >= fromYear && c.Year <= toYear))
{
Console.Out.WriteLine(car);
}
Note how the functionality of your GetCars() can be expressed in a fairly readable Linq Where method call applied to the list.
As #Ciara specified, you must print each item yourself. When you issue something like Console.WriteLine(car) in your code, the Console class automatically calls ToString() method on your Car object.
The documentation for ToString() method on MSDN specifies:
The default implementation of the ToString method returns the fully qualified name of the type of the Object [...]
In your case, that name is System.Collection.Generic.List'1[Lab__2.Car]. To change what ToString() method returns, in your Car class override the method:
public class Car
{
public string Make { get; set; }
public int Year { get; set; }
public override string ToString()
{
return String.Format("Make: {0}, Year: {1}", Make, Year);
}
}
Afterwards you need to iterate the list and print each item:
carList.ForEach(Console.WriteLine);
Related
I have a simple Serializeable class called "PlayerTutorial":
public class PlayerTutorial
{
public string ItemName { get; set; }
public string ItemTag { get; set; }
public override string ToString()
{
return $"{ItemName} {ItemTag}";
}
}
After player input I save the class to a List data, using another script which is responsible for saving and Loading.
Upon Loading, I'd like to see in the console the two strings I've saved to the list.
For example:
ItemName = "Tea", ItemTag = "HotDrink"
ItemName = "IceTea", ItemTag = "ColdDrink"
What method should I use to represent the items in my list of the class?
I tried using:
Debug.Log(string.Join(",", data.Select(d => d.ItemName)));
Only shows the first item of each saved class (e.g: "Tea", "IceTea")
I think I should try the data.SelectMany
but can't implement it correctly since I seem to be missing some parent object to do so.
You are actually overriding .ToString() which is the result you're expecting. so to get tho that result you only need to call either the data.ToString() or directly dont call any method, as by default the next statement: Debug.Log(string.Join(",", data)); will call .ToString() for each element on the list.
I want to create a method that displays the information contained in an object, that will work dynamically, with any object. I'm having trouble handling properties that are other custom classes. In the example below the Person has Phones and Occupations which both are other classes. When the data is displayed, the value on the screen currently is:
TestReflection.Person
Name: Mary
Phones: TestReflection.Phones
Occupations: TestReflection.Occupations
It just displays the name of class, like TestReflection.Phones, rather than the data inside that object.
How can I change this code to show information like this instead?
TestReflection.Person
Name: Mary
Phones:
TestReflection.Phones
Type: 1
Number: 555XYZ
Occupations:
TestReflection.Occupations
Type: 5
Description: Secretary
Here is my code:
class Program
{
static void Main(string[] args)
{
List<Person> listPeson = new List<Person>();
var person1 = new Person();
person1.Name = "Mary";
person1.Phones = new Phones { new Phone { Type = 1, Number = "555XYZ" } };
person1.Occupations = new Occupations {new Occupation { Type = 5, Description = "Secretary" }};
listPeson.Add(person1);
DynamicExport(listPeson);
Console.ReadLine();
}
public static void DynamicExport<T>(List<T> listReg)
{
for (int i = 0; i < listReg.Count; i++)
{
Console.WriteLine(listReg[i].GetType());
foreach (var item in listReg[i].GetType().GetProperties())
{
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
}
}
}
}
class Person
{
public string Name { get; set; }
public Phones Phones { get; set; }
public Occupations Occupations { get; set; }
}
class Phones : List<Phone> { }
class Phone
{
public int Type { get; set; }
public string Number { get; set; }
}
class Occupations : List<Occupation> { }
class Occupation
{
public int Type { get; set; }
public string Description { get; set; }
}
I made some edits to your question - I hope I understood you correctly.
If you want to export data
If your question is really about displaying data, then there are better ways to do it than creating your own export method. The format you are trying to display looks similar to YAML. There's also JSON and XML. Using one of these libraries is probably better than writing your own method:
YamlDotNet NuGet package
Json.NET NuGet Package
System.Xml.Serialization.XmlSerializer class
If you want to learn more about reflection
Maybe you're interested in learning more about reflection, and the export is just an example to play around with it. In that case, let's look at this line:
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
$"{item.GetValue(listReg[i], null)}" ends up calling person1.Phones.ToString(). The default behavior of ToString just displays the type name. You could override that behavior, like this:
class Phones : List<Phone>
{
public override string ToString()
{
return Program.DynamicExportToString(this);
// ... where DynamicExportToString is a modified version of DynamicExport that
// builds and returns a string rather than sending it directly to the Console.
}
}
Maybe you want to be able to handle any class, even when you cannot override ToString in all of the classes you might export. Then you will need to put some additional logic in the DynamicExport method, because...
$"{item.Name}: {item.GetValue(listReg[i], null)}"
... doesn't work for every situation. We need to display different things depending on the type of the property.
Consider how you want to handle null values. Maybe something like $"{item.Name}: <null>"
Use your existing $"..." code if the type is...
a primitive type.
DateTime
String
... or a Nullable<> of one of those types.
If the type implements IEnumerable, loop over the contents of the collection and recursively call your export code for each element.
It's important to check for this interface after you've checked if the type is a String, because String implements IEnumerable.
Otherwise, recursively call your export code on this value.
When you call your export code recursively, it would be wise to guard against infinite loops. If the object you're trying to export contains a circular reference - you could quickly wind up with a StackOverflowException. To avoid this, maintain a stack of objects that have already been visited.
I think the above advice is generally applicable whenever you're using reflection to traverse an object graph - whether it's for serialization or any other purpose.
I hope this helps!
I created my class
class TxtEmail
{
public TxtEmail(string firtFirstmail, string domain)
{
this.Firstmail = firtFirstmail;
this.Domain = domain;
}
public string Firstmail { get; set; }
public string Domain { get; set; }
public string RetOneString()
{
return Firstmail + "#" + Domain;
}
}
Then my class add to List Class
class EmailDP : List<TxtEmail>
{
List<TxtEmail> txtemail = new List<TxtEmail>();
public void Add(string path)
{
txtemail.Add(new TxtEmail("user1", "google.ru"));
txtemail.Add(new TxtEmail("user5555", "google.com"));
txtemail.Add(new TxtEmail("user252", "outlook.com"));
txtemail.Add(new TxtEmail("user3", "gmail.com"));
}
another methods ......
But then i created object my classes, he show 0 Count, Why? Where i make mistake and how i can get object in it?
EmailDP em1 = new EmailDP();
MessageBox.Show(em1.Count.ToString()); -> this show 0
foreach (var myob in em1)
{
MessageBox.Show(myob.RetOneString());
}
You have two lists involved:
The class EmailDP itself derives from List<TxtEmail> and is therefore a list whose count you are displaying
The internal list txtemail to which you actually add the items, leaving EmailDP itself empty.
Change your class, so that it encapsulates the inner list.
class EmailDP
{
private readonly List<TxtEmail> _txtemail = new List<TxtEmail>();
public void Add(string path)
{
_txtemail.Add(new TxtEmail("user1", "google.ru"));
_txtemail.Add(new TxtEmail("user5555", "google.com"));
_txtemail.Add(new TxtEmail("user252", "outlook.com"));
_txtemail.Add(new TxtEmail("user3", "gmail.com"));
}
public int Count => _txtemail.Count;
public IEnumerable<TxtEmail> EmailTexts => _txtemail;
// ... other methods
}
And of course you have to call Add at least once...
EmailDP em1 = new EmailDP();
em1.Add("some path");
em1.Add("another path");
MessageBox.Show(em1.Count.ToString());
foreach (var myob in em1.EmailTexts)
{
MessageBox.Show(myob.RetOneString());
}
If you override ToString instead of creating a method RetOneString, you have the advantage that the TxtEmail objects will be displayed automatically at several places. I.e. in the debugger, in listboxes or in Console.WriteLine.
public override string ToString()
{
return Firstmail + "#" + Domain;
}
Your EmailDP is-a List and also has-a List and I don't think you mean for both of those to be true. It's a question of inheritance vs composition. Do you need your class to be a list or can it contain a list instead?
If you want your class to be-a List, then in your add method, you can do:
this.Add(new TxtEmail("user1", "google.ru")); ...
If you want your class to contain-a List then you can remove the inheritance of List
class EmailDP
{
...
And then make your list public so it is accessible:
public List<TxtEmail> txtemail = new List<TxtEmail>();
And then get the count from the list contained within:
MessageBox.Show(em1.textemail.Count.ToString());
Hope that helps.
You need to call the Add Method which fills your array.
Also do not inherit the List as you do not need this.
EmailDP em1 = new EmailDP();
em1.Add(string.Empty);
MessageBox.Show(em1.Count.ToString()); -> You will get items you added in the Add Method
foreach (var myob in em1)
{
MessageBox.Show(myob.RetOneString());
}
first of all congrats for the web, this is my first question but I have found a lot of answer before here.
Here my problem: I have to take some data from an excel file and print them off in a file. Until here everything is ok. But depending on the order of the file list, I take the data in one or another order. My proposal is after all the data have been taken from the excel, to sort those data by the date of the bill (one of the elements of the data). I am describing my classes below.
I have my Bill class:
class Bill
{
private string billNumber;
private string billDate;
private DateTime date;
private string from;
private string to;
private string billCost;
private string power1;
private string power2;
private string power3;
private string power4;
private string power5;
private string power6;
private string contractNumber;
}
And this is my Contract Class:
class Contract
{
private List<Bill> billList;
private Dictionary<double, Bill> billsDictionary;
private double contractNumber;
}
After in my program I have a List<Contract>.
I would like to sort the List<Bill> of the Contract class by the date, to be able to print the bill in the correct order, but I have not been able to find any solution to order them.
Thanks in advance.
Greetings.
There are two solutions. First you need to expose property that can be used to sort.
public class Bill
{
// ... rest of your code
public DateTime Date
{
get
{
return this.date;
}
}
}
1) You can use List's Sort with passed comparer (msdn):
public class Comp : IComparer<Bill>
{
public int Compare(Bill x, Bill y)
{
// remember to handle null values first (x or y or both are nulls)
return x.Date.CompareTo(y.Date);
}
}
then:
billList.Sort(new Comp());
Keep in mind: if you declare Comp as nested class of Bill you won't have to add property Date. You will be able to see private fields of Bill and use it for comparsion..
2) You can use linq (it creates new instance of List):
billList = billList.OrderBy(bill => bill.Date).ToList();
You should be able to do that using linq like q = q.OrderBy(a => a.ColumnName) but you may want to check the access modifiers of your properties, being private you won't be able to access them from the outside.
Here's a simple example on ordering with Linq:
class Pet
{
public string Name { get; set; }
public int Age { get; set; }
}
public static void OrderByEx1()
{
Pet[] pets = { new Pet { Name="Barley", Age=8 },
new Pet { Name="Boots", Age=4 },
new Pet { Name="Whiskers", Age=1 } };
IEnumerable<Pet> query = pets.OrderBy(pet => pet.Age);
foreach (Pet pet in query)
{
Console.WriteLine("{0} - {1}", pet.Name, pet.Age);
}
}
/*
This code produces the following output:
Whiskers - 1
Boots - 4
Barley - 8
*/
Notice how the properties of the class are public so you can access them from anywhere in the code.
More info on sorting can be found here.
I created a class that had one member of type List. I could then add to this using ClassNameInstance.Add().
We are now using some code from a third-party that will automatically use any class I create and its values. Except lists. It only returns the first element in the list.
What I need is a comma-separated version returned instead for this third-parties code to use. Now I could just append the Strings to a String member, but this doesn't look as nice as the .Add() method.
So I wanted to create my own Class that I could have an Add() method for but could access its single value like so:
MyClass1.Add("Test1");
MyClass1.Add("Test2");
Console.WriteLine(MyClass2);
The output I would like would be Test1, Test2. I hope this makes sense!
UPDATE1:
Seems the above may not have been clear.
public class MyClass1
{
????
}
public class MyClass2
{
MyClass1 mc1 { get; set; }
String name { get; set; }
}
The third party code will use MyClass2 and its assigned values. When I used List instead of MyClass1 I only get the first value in the list, but need a CSV list as a String returned.
MyClass2 mc2 = new MyClass2();
mc2.mc1.Add("Test1");
mc2.mc1.Add("Test2");
Console.WriteLine(mc2.mc1) should output -> Test1, Test2
Hope that clears things up some more!
Thanks everyone! :)
UPDATE2:
It seems everyone is suggesting the same thing - use ToString().
Unfortunately, the third-party code will look at my class and determines the members type and value automatically. This means that I am not able to pass the code the value that would be returned by calling ToString().
I kind of need the add/remove functionality of a List<> but when used its value returns as a single CSV string.
I'm not sure that you need an extra class here:
List<string> list = new List<string>();
list.Add("Test1");
list.Add("Test2");
Console.WriteLine(string.Join(", ", list.ToArray());
You could wrap this behaviour in a class just to get it automatically invoked on ToString:
public sealed class StringJoiningList
{
private readonly List<string> list = new List<string>();
public void Add(string item)
{
list.Add(item);
}
public override string ToString()
{
return string.Join(", ", list.ToArray());
}
}
Now that's assuming you just want a List<string> - it also doesn't let you access the items other than by the ToString() method. All of that is fixable of course, if you could give more information. You could even just derive from List<T>:
public class StringJoiningList<T> : List<T>
{
public override string ToString()
{
return string.Join(", ", this.Select(x => x.ToString()).ToArray());
}
}
I'm not entirely sure I like the idea, but it depends on what you need to do...
A couple of easy options would be to override the ToString() method of your class (so that when it is used in this context it returns a comma-delimited list of items) or you can provide a property that flattens the list.
You need to implement the ToString method.
Here's an extension I wrote to join the items in an IEnumerable<T>
public static string Join<T>(this IEnumerable<T> collection,
string separator, Func<T, object> selector)
{
if (null == separator)
throw new ArgumentException("separator");
if (null == selector)
throw new ArgumentException("selector");
Func func = item =>
{
var #object = selector(item);
return (null != #object) ? #object.ToString() : string.Empty;
};
return string.Join(separator, collection.Select(func).ToArray());
}
It can be used like this:
List<Person> list = new List<Person>()
{
new Person() { FirstName = "Magic", LastName = "Johnson" },
new Person() { FirstName = "Michael", LastName = "Jordon" },
new Person() { FirstName = "Larry", LastName = "Bird" }
};
var value = list.Join(", ", p => p.FirstName);
// value is "Magic, Michael, Larry"
Not really the answer I wanted, but it seems that I couldn't do what I wanted.
In the class where I wanted to have my second class as a member, I made that member a String so the third-party code would work as I wanted.
I created a second class that had a List<String> as a public member and then a Method called .ToCSV() to output the list as a csv string. I added a .Add() method to add the strings to the list to save having to go Class1.Class2.Add().
Then just before the third-party code would use my class, I do:
MyClass1.MyString = MyClass2.ToCSV();
Thus giving me basically what I wanted in a round about way!!
Thanks for everyone's input and help!
Neil
The simplest solution is probably to inherit from the StringCollection class and just override ToString. Otherwise this class already has everything you are asking about.
public class MyClass : System.Collections.Specialized.StringCollection
{
public override string ToString()
{
var c = new string[this.Count];
this.CopyTo(c,0);
return string.Join(",", c);
}
}
Then you can use it just like you were trying to before. No need to get fancy with generics for this simple usage.
Edit:
Classes themselves don't have a return value, so the 3rd party code must be reading properties. You should be able to override ToString() as described above then expose a property that returns the ToString(). That would probably make the string value visible to the 3rd party code
public class MyClass : System.Collections.Specialized.StringCollection
{
public string MyValue
{
get { return this.ToString(); }
}
public override string ToString()
{
var c = new string[this.Count];
this.CopyTo(c,0);
return string.Join(",", c);
}
}