This is a C# winform project where I have "Car" objects that I am adding to a ListBox. I can add the objects and even see the properties one by one using something like this:
Car car = (Car)listBox1.SelectedItem;
Console.Write(car.Name);
But how can I get, for instance, the BodyColor property from all cars? I attempted to use foreach with no success. It simply returned the name of the Class for each loop instead of a specific property. Any help would be greatly appreciated.
Here is my code thus far:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace string_test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Car car1 = new Car();
car1.Name = "Bobs Car";
car1.BodyColor = Color.Black;
car1.IsSedan = true;
car1.TopSpeed = 110;
Car car2 = new Car();
car2.Name = "Bills Car";
car2.BodyColor = Color.Red;
car2.IsSedan = false;
car2.TopSpeed = 140;
listBox1.Items.Add(car1);
listBox1.Items.Add(car2);
}
private void button1_Click(object sender, EventArgs e)
{
// Here is where I am trying to get all BodyColor properties
// from every car in the ListBox.
// What I have below gets the BodyColor from whatever
// car is selected instead.
Car car = (Car)listBox1.SelectedItem;
Console.Write(car.BodyColor);
}
class Car
{
public string Name { get; set; }
public int TopSpeed { get; set; }
public bool IsSedan { get; set; }
public Color BodyColor { get; set; }
public override string ToString()
{
return Name;
}
}
}
}
I would recommend using either LINQ queries or LINQ methods for something like this. FYI, in case you don't know what LINQ is, it enables you, as a programmer, to make equivalent calls that could be in SQL queries, on C# structures that implements the interface IEnumerable. More on this here.
LINQ METHODS
var carsFromBodyColor = listbox.Items.Where(obj=> obj!= nulll).Cast<Car>().Select(castedCar => castedCar.BodyColor);
foreach(var c in carsFromBodyColor)
Console.WriteLine(c.BodyColor);
LINQ QUERIES
var carColors = from Car carItem in listBox.Items
where carItem != null
select carItem .BodyColor;
foreach(var color in carColors)
Console.WriteLine(color);
Use listbox.Items to get all items.
Use LINQ Cast and Select to get list of BodyColors:
listBox1.Items.Cast<Car>().Select(c => c.BodyColor);
Related
Background
I'm not sure my question in the title is actually expressed properly, but here's what I'm trying to do. (I am using a bogus analogy here to represent real code written by the team. We aren't allowed to post actual code. But I have tried to ensure that the concepts I see in our code base have been transferred over to this example)
I have some vehicle records in my database... some of them are cars, others will be trucks etc. I also have some controllers - endpoints for each vehicle type. So I have a Car controller that allows specific methods just for cars.
All of the columns in the database are identical for all types of vehicles. The vehicle DTO class looks like this:
public class Vehicle
{
[Key]
public int Id { get; set; }
[Column]
public int BrandName{ get; set; }
[Column]
public string VehicleDetails{ get; set; }
public IDictionary<string, object> VehicleDetailsJson
{
get
{
return (IDictionary<string, object>)JsonConvert.DeserializeObject<List<ExpandoObject>>(VehicleDetails).FirstOrDefault();
}
}
But in the case of a car, we need additional fields. so we have this:
public class Car : Vehicle
{
public static List<string> Models {get;} = new()
{
"two-seater",
"four-seater",
"six-seater"
}
public bool doSomethingToTheBrandName()
{
//does something to the brand string on the base class.
return true;
}
}
Because all the database columns that are being return are the same, I'm using the vehicle model as a return type in my repository class. This is how I pull the data from the database: (pseudo-ish code)
public class VehicleRepository: GenericRepository<Vehicle>, IVehicleRepository
{
private readonly string encryptionPassword;
public VehicleRepository(AppSettings appSettings) : base(appSettings)
{
encryptionPassword = appSettings.EncryptionPassword;
}
public List<Vehicle> GetVehicles(string brandName= "")
{
var vehicles = new List<Vehicle>();
var searchFilter = !string.IsNullOrEmpty(brand )
? $"BrandName = #Brandname"
: "BrandName = 'all'";
vehicles = Retrieve().ToList();
return vehicles ;
}
Here's the interface for the rpository:
public interface IVehicleRepository : IGenericRepository<Vehicle>
{
List<Vehicle> GetVehicles(string brandName = "");
}
Problem
Where I'm running into issues is trying to call the repository from a car controller. This is where I'm not quite sure how to design / write the code.
The car controller uses requests and responses like this:
public class CarRequest: Request
{
public string brandName{ get; set; }
}
public class CarResponse: Response
{
public List<Car> Cars { get; set; } //notice we are returning a car, not vehicle.
}
And here's the controller itself:
public class CarController: ControllerBase
{
private readonly AppSettings appSettings;
private readonly IVehicleRepository vehicleRepository;
public CarResponse GetCars (CarRequest request)
{
try
{
var response = new GetCarResponse();
response.Cars = vehicleRepository.Vehicles("ford");
}
}
Naturally, the system fails trying to save the response from the database call because I'm trying convert a vehicle into a car.
Questions
Which class should be responsible for casting from the base type
"Vehicle" to "Car".
Also in the future, if there are Car specific methods that we need to write in a repository class, should we just do that inside the vehicle repo class?
Anything else that smells off?
EDIT 1
So my real code actually has an if statement baked in (I'm including the suggested refactors)
public List<T> GetVehicles<T>(string brandName= "", int someFlag)
{
var vehicles = new List<T>();
if (someflag==0)
{
var searchFilter = !string.IsNullOrEmpty(brand )
? $"BrandName = #Brandname"
: "BrandName = 'all'";
vehicles = Retrieve().OfType<T>.ToList();
}
else
{
vehicles.Add(Retrieve(someFlag));
}
return vehicles ;
}
I'm sorry but I'm still kinda new to OO and c#.
So I wasn't sure if by TVehicle you meant T. I've seen before.
But the error I'm getting on this line:
vehicles = Retrieve().OfType<T>.ToList();
is
CS0119 'Enumerable.OfType(IEnumerable)' is a method, which is
not valid in the given context
and I haven't attempted to fix the Else path but that also shows the following error:
CS1503 Argument 1: cannot convert from 'Widgets.Models.Vehicle' to
'T'
EDIT 2
public List<TVehicle> GetVehicles<TVehicle>(string brandName= "", int someFlag)
where TVehicle: Vehicle
{
var vehicles = new List<TVehicle();
if (someflag==0)
{
var searchFilter = !string.IsNullOrEmpty(brand )
? $"BrandName = #Brandname"
: "BrandName = 'all'";
vehicles = Retrieve().OfType<TVehicle>.ToList();
}
else
{
vehicles.Add(Retrieve(someFlag));
}
return vehicles ;
}
But I'm still getting the same two errors on the same two lines.
One of the solution to this is can be that, we will need to define the method generic so that we can pass the type from calling side something like:
public List<TVehicle> GetVehicles<TVehicle>(string brandName= "")
{
var vehicles = new List<TVehicle>();
var searchFilter = !string.IsNullOrEmpty(brand )
? $"BrandName = #Brandname"
: "BrandName = 'all'";
vehicles = Retrieve().OfType<TVehicle>.ToList();
return vehicles ;
}
and now on calling side it should be something like:
var cars = GetVehicles<Car>("brand name");
Generally it is a good practice to have a separated repo for each domain entity. So it may worth creating an abstract Vehicle repository and derive other repose from it (not tested):
public abstract class VehicleRepository<TVehicle>: GenericRepository<TVehicle>, IVehicleRepository
where TVehicle:Vehicle
{
private readonly string encryptionPassword;
public VehicleRepository(AppSettings appSettings) : base(appSettings)
{
encryptionPassword = appSettings.EncryptionPassword;
}
public List<TVehicle> GetVehicles(string brandName= "")
{
var vehicles = new List<TVehicle>();
var searchFilter = !string.IsNullOrEmpty(brand )
? $"BrandName = #Brandname"
: "BrandName = 'all'";
vehicles = Retrieve().ToList();
return vehicles ;
}
and then define TruckVehicle, CarVehicle ,.. inheriting from that class.
Im new to c#, I tried googling but no answers. Its flaging me an error when I use this keyword. (Should i just take it out and use Car instead?) Its also flaging an error when I use Public Car(). I tried talking out Public and it worked but I need to use it in different Files. It could be because im following an outdated course.
using System.Text;
using System;
namespace SimpleClasses
{
class Program
{
static void Main(string[] args)
{
Car myNewCar = new Car();
myNewCar.Make = "toyota";
myNewCar.Model = "Cutlas Supreme";
myNewCar.Year = 1986;
myNewCar.Color = "Silver";
Car myOtherCar = myNewCar;
Console.WriteLine(myOtherCar.Make);
Console.WriteLine("Before: " + myNewCar.Make);
doByValue(myNewCar);
Console.WriteLine("After By Value: " + myNewCar.Make);
doByReference(ref myNewCar);
Console.WriteLine("After By Reference: " + myNewCar.Make);
static void doByValue(Car car)
{
car = new Car();
car.Make = "BMW";
}
static void doByReference(ref Car car)
{
car = new Car();
car.Make = "BMW";
}
Console.ReadLine();
public Car()
{
this.Make = "Nissan";
}
}
}
class Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public string Color { get; set; }
public double DetermineMarketValue()
{
return 0.0;
}
}
}
This code is a constructor for the Car class, it does not belong in the static main method. Move it inside the Car class:
class Car
{
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public string Color { get; set; }
public Car()
{
this.Make = "Nissan";
}
public double DetermineMarketValue()
{
return 0.0;
}
}
Though this code would intitialize every car instance as a Nissan make. Normally you would use the constructor to ensure all required values in a new instance are provided to help ensure that a Car instance is always in a valid state. For example:
public Car(string make, string model, int year, string color = "White")
{
Make = !string.IsNullOrEmpty(make) ? make : throw new ArgumentNullException(nameof(make));
Model = !string.IsNullOrEmpty(model) ? model : throw new ArgumentNullException(nameof(model));
Year = year >= 1940 && year <= DateTime.Today.Year ? year : throw new ArgumentOutOfRangeException(nameof(year));
Color = color; // Defaults to "White".
}
This requires any code wanting to create an instance of a Car would need to provide all of the required details about the car to help avoid a situation where an invalid/incomplete object ends up getting passed around. It's a good idea to validate values being passed in to catch potential problems early.
In C#, be mindful of scopes for classes, methods, and namespaces, the use of { and }.
Code like this:
static void doByValue(Car car)
{
car = new Car();
car.Make = "BMW";
}
static void doByReference(ref Car car)
{
car = new Car();
car.Make = "BMW";
}
inside the scope of the Main method also does not make any sense.
The static main method is a function that will execute when your application starts. "Car" is a class, meaning an encapsulation for properties and functions applicable to a Car. Think of a Class as a template or blueprint for a thing and/or the instructions for how that thing should be used. The Main function may create one or more instances of Car "objects". Classes are the templates, Objects are the instances of those classes.
Hopefully that helps make some sense around classes and instances.
I'm trying to learn how the lists work and I'm quite lost on a concept of using set; get; properties and working with a List. I have a project that I want to populate a list with 2 items that are related to each other (hence a object) Relation and an ID so I created a RelationLink class, the idea is to populate list with these 2 prosperities but using (get; set;) and I'm not quite sure how to do this so it would let me add the properties to a list and retrive a list from a PropertiesRepository as well.
public class PropertiesRepository
{
public class RelationLink
{
public string Relation { get; set; }
public string LinkID { get; set; }
}
public class ListofRelations
{
public List<RelationLink> relList { get; set; }
public void addRelation(RelationLink rel)
{
relList.Add(rel);
}
}
}
the code below fails at listofRelations.addRelation(relationLink) when debugging I can see that its going to add addRelation method and I see the values being passed. However when adding the object to a list nothing is being added. so when get runs it fails due to null exception.
am I doing the setters getters correctly? the part where I'm lost is how can I add the 2 (string) properties to that list in a main program file with setters, that could be read from relList
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
PropertiesRepository repProperties = new PropertiesRepository();
PropertiesRepository.RelationLink relationLink = new PropertiesRepository.RelationLink();
PropertiesRepository.ListofRelations listofRelations = new PropertiesRepository.ListofRelations();
relationLink.Relation = "Relation A";
relationLink.LinkID = "12345";
listofRelations.addRelation(relationLink);
foreach (var elm in listofRelations.relList)
{
Console.WriteLine("Relation from List is " + elm.Relation + "Link ID from List is " + elm.LinkID);
}
}
}
relList in your instance of listofRelations is never initialised with an instance of the list.
public class ListofRelations
{
public List<RelationLink> relList { get; set; } = new List<RelationLink>();
public void addRelation(RelationLink rel)
{
relList.Add(rel);
}
}
you could initialise it like this or in a constructor. Or before you call addRelation you could write if you want.
listOfRelations.relList = new List<RelationLink>();
Step 1: I have created a C# application called : Student details
Step 2: Added four TextBoxes and named them as :
Image below to refer:
Studentname.Text
StudentSurname.Text
StudentCity.Text
StudentState.Text
DATA INSIDE CSV FILE
vikas,gadhi,mumbai,maharashtra
prem,yogi,kolkata,maha
roja,goal,orissa,oya
ram,kala,goa,barka
Issue is How do I fetch all the data(surname,city,state) of user prem into above textboxes studentsurname,studentcity,studentstate from csv file when I search the name in textbox 1 => studentname.Text as prem
Below is the Code where I am stuck at return null and code inside Load_Script_Click
void Connection_fetch_details(String searchName)
{
var strLines = File.ReadLines(filePath);
foreach (var line in strLines)
{
if (line.Split(',')[0].Equals(searchName))
{
Connection_fetch_details cd = new Connection_fetch_details()
{
username = line.Split(',')[1]
};
}
}
return;
}
private void Load_Script_Click(object sender, EventArgs e)
{
// load script is button
String con_env = textenv.Text.ToString();
//Address Address = GetAddress("vikas");
//textsurname.text = Address.Surname
Connection_fetch_details cd = Connection_fetch_details(con_env);
textusername.Text = cd.username;
}
==============================================================
Class file name : Address.class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DDL_SCRIPT_GENERATOR
{
public class Connection_fetch_details
{
public string username { get; set; }
}
}
The main problem is that your method is void, which means it doesn't return any value. So even though you may be finding a match, and creating a Connection_fetch_details object, you aren't returning that result back to the calling method.
This will fix that problem:
Connection_fetch_details Connection_fetch_details(String searchName)
{
var strLines = File.ReadLines(filePath);
foreach (var line in strLines)
{
if (line.Split(',')[0].Equals(searchName))
{
Connection_fetch_details cd = new Connection_fetch_details()
{
username = line.Split(',')[1]
};
return cd; //return the object containing the matched username
}
}
return null;
}
Now it will return a Connection_fetch_details object if there is a match, or null if there is no match.
Next, you asked about returning all the fields, not just one. For that you would need to
a) add more properties to your object
b) add more code to populate those properties from the CSV
c) add code to populate the textboxes with the results from the object.
I'm also going to rename "username" to something more relevant, since none of the field names you described in the question match that. I'm also going to rename your class to "Student", and rename your search method, for the same reason.
Here's an example:
Student searchStudent(String searchName)
{
var strLines = File.ReadLines(filePath);
foreach (var line in strLines)
{
var split = line.Split(',');
if (split[0].Equals(searchName))
{
Student s = new Student()
{
firstname = searchName,
surname = split[1],
city = split[2],
state = split[3]
};
return s; //return the object containing the matched name
}
}
return null;
}
private void Load_Script_Click(object sender, EventArgs e)
{
// load script is button
String con_env = textenv.Text.ToString();
//Address Address = GetAddress("vikas");
//textsurname.text = Address.Surname
Student st = searchStudent(con_env);
textsurname.Text = st.surname;
txtcity.Text = st.city;
txtstate.Text = st.state;
}
namespace DDL_SCRIPT_GENERATOR
{
public class Student
{
public string firstname { get; set; }
public string surname { get; set; }
public string city { get; set; }
public string state { get; set; }
}
}
To accomplish your goal you have to further separate your problem in more granular steps and also distinguish between what you show in your UI and what informations you hold in the background in which format.
Create a class with the desired properties
public class Student { public string Name { get; set; } ... }
Learn how to read a csv file into such an object by using an existing library like CsvHelper or CsvReader.
When you have something like List<Student> from this part. Learn how you can visualize such a thing by using some Binding (also depends on the visualization you use Winforms, WPF, etc.).
Depending on the visualization component it already supports filtering or you need to filter by yourself by using e.g. LINQ to get the matching elements students.Where(student => student.Name.StartsWith(search)).
So far a lot of smaller problems which is simply to much to answer in a single one. Please try to break down your problems into smaller ones and search for their solutions. If you get stuck, ask a new question. That's all I can do for you now.
I'm new to C# programming and have hit a snag I cannot get past.
I'm getting this compile error:
CS0305: Using the generic type 'System.Collections.Generic.IEnumerable' reuires 1 type arguments
with this code;
class Program
{
static void Main(string[] args)
{
Car c = new Car();
c.PetName = "Frank";
c.Speed = 55;
c.colour = "Green";
Console.WriteLine("Name = : {0}", c.PetName);
c.DisplayStats();
Garage carLot = new Garage();
// Hand over each car in the collection
foreach (Car c in carLot)
{
Console.WriteLine("{0} is going {1} MPH",
c.PetName, c.CurrentSpeed);
}
Console.ReadLine();
}
class Car
{
//Automatic Properties
public string PetName { get; set; }
public int Speed { get; set; }
public string colour { get; set; }
public void DisplayStats()
{
Console.WriteLine("Car Name: {0}", PetName);
Console.WriteLine("Speed: {0}", Speed);
Console.WriteLine("Color: {0}", colour);
}
}
public class Garage
{
private Car[] CarArray = new Car[4];
// Fill with some car objects on startup.
public Garage()
{
carArray[0] = new Car("Rusty", 30);
carArray[1] = new Car("Clunker", 55);
carArray[2] = new Car("Zippy", 30);
carArray[3] = new Car("Fred", 30);
}
}
public IEnumerator GetEnumerator()
{
foreach (Car c in carArray)
{
yield return c;
}
}
}
How can I resolve this?
There are two variants of IEnumerable, the generic one (which is in the System.Collections.Generic namespace) accepts a type argument which specified the types of objects that the enumerable contains. The other one (contained in the System.Collections namespace) has no type argument and so exposes the type object - you appear to be declaring / using the non-generic variant, however are not using the System.Collections namespace.
I think the quick way to fix your particular compile error is to put the following at the top of your source code file:
using System.Collections;
Alternatively you can instead use the Generic version (which you should try to do wherever possible as it is type safe) by specifying type parameters when you declare IEnumerable, like this:
IEnumerable<Car>
IEnumerator<Car>
You might also want to read An Introduction to C# Generics
You also seem to have a few more errors than that, but these seem like they might be from problems copying and pasting the source (specifically Garage does not implement IEnumerable, either the generic or non-generic version, and GetEnumerator is on the Program class, not the Garage class).
You have more errors than just that. But specifically for that error, you're looping over Garage in a foreach, but that class does not expose an enumerator, mainly because the method GetEnumerator is actually outside of the class. Move the method inside Garage and then you'll be able to get all the way to scene of the next crash.
Actually, for that error, you need using System.Collections; and then you need to move the GetEnumerator method. Like I said, you have tons of errors in this code.
You have a lot of typos. As others have said, your specific answer is you need to add ": IEnumerable" to your class Garage statement.
Here is the code fixed enough to compile cleanly:
class Program
{
static void Main (string[] args)
{
Car c = new Car ("Frank", 55);
c.colour = "Green";
Console.WriteLine ("Name = : {0}", c.PetName);
c.DisplayStats ();
Garage carLot = new Garage ();
// Hand over each car in the collection
foreach (Car ch in carLot) {
Console.WriteLine ("{0} is going {1} MPH", ch.PetName, ch.Speed);
}
Console.ReadLine ();
}
class Car
{
//Automatic Properties
public string PetName { get; set; }
public int Speed { get; set; }
public string colour { get; set; }
public void DisplayStats ()
{
Console.WriteLine ("Car Name: {0}", PetName);
Console.WriteLine ("Speed: {0}", Speed);
Console.WriteLine ("Color: {0}", colour);
}
public Car(string petname, int speed) { PetName = petname; Speed = speed; }
}
public class Garage : IEnumerable
{
private Car[] carArray = new Car[4];
// Fill with some car objects on startup.
public Garage ()
{
carArray[0] = new Car ("Rusty", 30);
carArray[1] = new Car ("Clunker", 55);
carArray[2] = new Car ("Zippy", 30);
carArray[3] = new Car ("Fred", 30);
}
public IEnumerator GetEnumerator ()
{
foreach (Car c in carArray) {
yield return c;
}
}
}
}