BinarySearch error causing InvalidCastException - c#

I'm working on a homework assignment, everything works except the binary search. When the program executes, I get the following error in Friend temp = (Friend)o; of the IComparable: InvalidCastException was unhandled by user code. Unable to cast object of type 'System.String' to type 'FriendList.Friend'. I could use help to pinpoint where I'm misguided. Thanks in advance.
using System;
using System.Linq;
namespace FriendList
{
public static class FriendList
{
public static void Main()
{
var findname = "";
var friends = new Friend[8];
for (var i = 0; i < 8; i++)
{
Console.Write("Enter a name (or type quit to skip): ");
var name = Console.ReadLine();
if (name.ToLower() == "quit")
{
return;
}
Console.Write("Enter a phone number: ");
var phone = Console.ReadLine();
Console.Write("Enter a birth month number: ");
var month = Console.ReadLine();
Console.Write("Enter a birth day: ");
var day = Console.ReadLine();
Console.Write("Enter a birth year: ");
var year = Console.ReadLine();
friends[i] = new Friend
{
Name = name,
Phone = phone,
Month = Convert.ToInt32(month),
Day = Convert.ToInt32(day),
Year = Convert.ToInt32(year)
};
}
Array.Sort(friends);
for (int x = 0; x < 8; ++x)
{
Console.Write("{0} {1} {2}{3}{4}", friends[x].Name, friends[x].Phone, friends[x].Month, friends[x].Day, friends[x].Year);
Console.Write("\n");
}
Console.ReadKey();
var findfriends = new Friend();
Console.Write("Enter the name you want for info:");
findname = Console.ReadLine();
for (int x = 0; x < 8; ++x)
{
x = Array.BinarySearch(friends, findname);
Console.Write("{0} {1} {2}{3}{4}", friends[x].Name, friends[x].Phone, friends[x].Month, friends[x].Day, friends[x].Year);
}
}
}
class Friend : IComparable
{
public string Name { get; set; }
public string Phone { get; set; }
public int Month { get; set; }
public int Day { get; set; }
public int Year { get; set; }
public Friend()
{ }
int IComparable.CompareTo(Object o)
{
Friend temp = (Friend)o;
return String.Compare(this.Name, temp.Name);
}
}
}

The issue is that you are trying to do a binary search on an array of type Friend to look for a String value. So when your ICompare implementation tries to cast the name to type friend it throws the exception you are seeing. To correctly use the ICompare you would need to create a new temporary friend object to pass into the binary search but just set the name like so:
Array.BinarySearch(friends, new Friend(){ Name = findName });

When you're calling
x = Array.BinarySearch(friends, findname);
you're passing findname, it is string. Here:
Friend temp = (Friend)o;
you're trying to cast this string to the Friend class.

Related

C# Assigning to strings from a public class and list

so far my code does the following.
Ask user for a numeric amount for 'players'
Then asks for names for each of the players which is added to a list and class
I'd like to call those names from the list or class (not really sure how class works) and assign it to a new string. Here's what I got so far:
public class NameVariable
{
public int ID { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
bool IsUserWrong = false;
Console.WriteLine("Write amount of players");
while (!IsUserWrong)
{
int TotalPlayers;
while (!Int32.TryParse(Console.ReadLine(), out TotalPlayers))
{
Console.WriteLine("Value must be numeric.");
}
if (TotalPlayers >= 12 && TotalPlayers <= 16)
{
List<NameVariable> PlayerList = new List<NameVariable>();
for (int index = 0; index < TotalPlayers; index++)
{
Console.WriteLine("Enter player {0}'s name:", index + 1);
PlayerList.Add(new NameVariable
{
Name = Console.ReadLine(),
ID = index
});
}
// string player1 = ???
// string player2 = ???
// and so on for 12-16 players
}
else
{
Console.WriteLine("Please enter a value between 12 and 16.");
}
}
}
}
I know that a foreach loop can be used to display all of the variables in the NameVariable class. Would just like to know how to assign each variable to a different string.
Before using the class I just used the list which worked by using
string player1 = PlayerList[0];
string player2 = PlayerList[1];
// and so on for the remaining players
Thanks in advance!
it's just
string player1 = PlayerList[0].Name;
string player2 = PlayerList[1].Name;
...
Essentially your list contains NameVariable objects. PlayerList[index] gives you the object, and .Name gives you the property value of the object.
If you want a specific player name by a specific ID number, you can use LINQ (just to give you a hint)
string player = PlayerList.Where(p => p.ID == WhateverIDNumber).First().Name;
While the answer to your immediate question, i.e., how to access properties of a class object, is as others have shown, I feel like this code has a bigger problem. That is you're trying to do too much in one function, namely, Main(). So I advice to in fact try and refactor your code so that one function does one thing. Something like:
public static int GetNumberOfPlayers()
{
Console.Write("Enter number of players: ");
int totalPlayers;
while (!Int32.TryParse(Console.ReadLine(), out totalPlayers))
{
Console.WriteLine("Value must be numeric.");
}
return totalPlayers;
}
public static List<NameVariable> GetPlayerList(int num)
{
var list = new List<NameVariable>();
for (int i = 0; i < num; i++)
{
Console.WriteLine("Enter player {0}'s name:", i + 1);
list.Add(new NameVariable
{
Name = Console.ReadLine(),
ID = i
});
}
return list;
}
public static void DisplayPlayers(List<NameVariable> list)
{
foreach(var player in list)
{
Console.WriteLine("Player {0}, Name: {1}", player.ID, player.Name);
}
}
public static void CantThinkOfAGoodName()
{
while (true)
{
int totalPlayers = GetNumberOfPlayers();
if (totalPlayers > 16 || totalPlayers < 12)
{
Console.WriteLine("Please enter a value between 12 and 16.");
}
else
{
var playerList = GetPlayerList(totalPlayers);
DisplayPlayers(playerList);
break;
}
}
}
public static void Main()
{
CantThinkOfAGoodName();
Console.ReadLine();
}
Not sure if it helps but you can use an indexer to get players by name.
public NameVariable this[string name]
Let's say you create a class for the colection
public class NameColection : List<NameVariable>
{
public NameVariable this[string name]
{
get
{
return this.FirstOrDefault(n => n.Name == name);
}
}
}
Then you access players by name
var players = new NameColection()
{
new NameVariable() { ID = 1 , Name = "John" },
new NameVariable() { ID = 2 , Name = "Paul" },
new NameVariable() { ID = 3 , Name = "George" },
new NameVariable() { ID = 4 , Name = "Ringo" }
};
var player1 = players["John"];
As NameColection inhertits from List, you will be able to add, remove or modify items the usual way.

Loop object creation

I need to have my second nested for loop send the array values to class friends.
I do not know how I would go about this?
Unless I missed something in the class?
namespace List
{
class Program
{
public const int ARRAYSIZE = 5;
static void Main()
{
string[] relSend = { "Enter name", "enter phone number", "enter 2 didigt month dob", "enter 2 digit day dob", "enter 2 digit dob year" };
string[] In = new string[5];
string[] answer = new string[10];
for (int x = 0; x <= 8; x++)
{
for (int i = 0; i < relSend.Length; i++)
{
WriteLine("{0}", relSend[i]);
In[i] = Console.ReadLine();
}
for (int i = 0; i < In.Length; i++)
{
}
}
}
}
}
public class Friends
{
public string Name { get; set; }
public int Phone { get; set; }
public int Month { get; set; }
public int Day { get; set; }
}
I guess you mean you want to create an object out of the information collected, that is no problem:
List<Friend> friends = new List<Friend>();
for (int x = 0; x <= 8; x++)
{
for (int i = 0; i < relSend.Length; i++)
{
WriteLine("{0}", relSend[i]);
In[i] = Console.ReadLine();
}
friends.Add( new Friend() { Name = In[0]
, Phone = int.Parse(In[1])
, Month = int.Parse(In[2])
, Day = int.Parse(In[3])
, Year = int.Parse(In[4])
}
);
}
Make sure to validate the input before creating the object! Also, I would suggest to use string for a phone number since you would lose the 0 that is the usual prefix. Month, Day and Year might be combined in a single DateTime.
Tell me if you need any explanations:
namespace List
{
class Program
{
//Create a dictionary to find out each question is realated to which property.
private static Dictionary<string, string> questions = new Dictionary<string, string>();
static void Main()
{
questions.Add("Enter name", "Name");
questions.Add("enter phone number", "Phone");
questions.Add("enter 2 didigt month dob", "Month");
questions.Add("enter 2 digit day dob", "Day");
questions.Add("enter 2 digit dob year", "Year");
//Create list of friends
List<Friends> friendsList = new List<Friends>();
for (int x = 0; x <= 8; x++)
{
Friends f = new Friends();
foreach (string q in questions.Keys)
{
Console.WriteLine("{0}", q);
//Find property using Sytem.Reflection
System.Reflection.PropertyInfo property = f.GetType().GetProperty(questions[q]);
//Set value of found property
property.SetValue(f, Convert.ChangeType(Console.ReadLine(), property.PropertyType), null);
}
//Add a friend to list
friendsList.Add(f);
}
}
}
}
public class Friends
{
public string Name { get; set; }
public int Phone { get; set; }
public int Year { get; set; }
public int Month { get; set; }
public int Day { get; set; }
}

Checking if A objects property exist

I was wanted to ask a question cause I can't really find what I'm looking for online. I want to see/check if a student IdNum already exist for example.
I don't know the right term that I'm looking for to google it, and the book I have isn't really that useful as to what to do when needing to do this sort of check.
Here is the code what i have been tried so far :
static void Main(string[] args)
{
Class1[] c1 = new Class1[10]
for (int i = 0; i < c1.Length; i++)
{
Console.WriteLine("enter Student ID");
string text = Console.ReadLine();
int value;
while (!Int32.TryParse(text, out value))
{
Console.WriteLine("ID Was not a Valid ID Number. Try Again");
text = Console.ReadLine();
}
// maybe here say if IdNum exist or not
{
// Try a different number
}
}
}
Class Class1
{
public int IdNum { get; set; }
public int SomethingElse { get; set; }
// and so on
}
Thanks
IEnumerable<Class1> c1 = GetStudents();
string text = Console.ReadLine();
int value;
while (!Int32.TryParse(text, out value))
{
Console.WriteLine("ID Was not a Valid ID Number. Try Again");
text = Console.ReadLine();
}
bool exist = c1.Any(s = > s.IdNum == value);
If you don't want to use linq, you can just rewrite the last line with:
bool exist = false;
foreach (var s in c1)
{
if (s.IdNum == value)
{
exist = true;
break;
}
}

Using IComparable

So I'm drawing a blank on this error.
Failed to compare two elements in the array.
The Array.Sort(patient); is where the error is accruing. I do have a IComparable interface, and a class file with the following code: Trying to sort by patient ID number
class Patient : IComparable
{
private int patientID;
private string patientName;
private int patientAge;
private decimal amount;
public int PatientId { get; set; }
public string PatientName { get; set; }
public int PatientAge { get; set; }
public decimal PatientAmount { get; set; }
int IComparable.CompareTo(Object o)
{
int value;
Patient temp = (Patient)o;
if (this.PatientId > temp.PatientId)
value = 1;
else if (this.PatientId < temp.PatientId)
value = -1;
else
value = 0;
return value;
}
}
and this is what's in my main method. Didn't add the Display() cause nothing added to it now, why it's commented out
private static void Main(string[] args)
{
int numOfPatients =2 ;
Patient[] patient = new Patient[numOfPatients];
for (int x = 0; x < numOfPatients; x++)
{
int intvalue;
decimal dollarValue;
patient[x] = new Patient();
Console.Write("Patient {0}: ", (x + 1));
Console.WriteLine("Enter the Patients ID: ");
bool isNum = int.TryParse(Console.ReadLine(), out intvalue);
if (isNum)
{
patient[x].PatientId = intvalue;
}
else
{
Console.WriteLine("Patient ID was invalid. ID needs to be numbers");
Console.WriteLine("Enter the Patients ID: ");
int.TryParse(Console.ReadLine(), out intvalue);
}
Console.WriteLine("Enter the Patients Name: ");
patient[x].PatientName = Console.ReadLine();
Console.WriteLine("Enter the Patients Age: ");
bool isAge = int.TryParse(Console.ReadLine(), out intvalue);
if (isAge)
{
patient[x].PatientAge = intvalue;
}
else
{
Console.WriteLine("Patient Age was invalid. Age needs to be numbers");
Console.WriteLine("Enter the Patients Age: ");
int.TryParse(Console.ReadLine(), out intvalue);
}
Console.WriteLine("Enter the Patients Amount Due: ");
bool isAmount = Decimal.TryParse(Console.ReadLine(), out dollarValue);
if (isAmount)
{
patient[x].PatientAmount = dollarValue;
}
else
{
Console.WriteLine("Patient amount Due was invalid. Amount needs to be a numbers");
Console.WriteLine("Enter the Patients Amount Due: ");
int.TryParse(Console.ReadLine(), out intvalue);
}
}
Array.Sort(patient);
Console.WriteLine("Patients in order with Amounts Owed are: ");
for (int i = 0; i < patient.Length; ++i) ;
//Display(patient[i], numOfPatients);
A few things come to mind:
a) Why not implement IComparable<Patient>?
b) Why re-implement int.CompareTo(int)? Your implementation of IComparable could just return this.PatientID.CompareTo(other.PatientID).
c) Are you sure the array is full when you're sorting it? I'm not sure what would happen if it contains null.
I would just write
return this.PatientId.CompareTo(temp.PatientId)
inside the overriden CompareTo method the class. No need to use equality symbols. This will do the int compare for you and return the correct value.
I also suggest that you just use some implementation of the IList class, and then you can use LinQ statements. Using this will prevent there ever being a null value in the "array"
It looks like if Array.Sort is passed with a typed array would call the Array.Sort<T>(T[]) overload. According to the MSDN documentation, this overload uses the IComparable<T> interface to compare objects. So it looks like you have two choices:
You can implement IComparable<T> instead of IComparable (better).
You could cast your array to Array to call the Array.Sort(Array) overload which uses the IComparable interface (worse).

Creating objects within a loop

I have been searching about creating a new object within a loop, found some answers and topics but its way too hard to understand. Making it with lists and arrays etc.
What I am trying to do is, get an input from the user(lets say 3), and create objects as many as the user will with the unique names. Like newperson1, newperson2, newperson3 etc.
My code looks like this:
class person
{
}
class Program
{
static void Main(string[] args)
{
Console.Write("How many persons you want to add?: ");
int p = int.Parse(Console.ReadLine());
for (int i = 0; i < p; i++)
{
person newperson = new person();
}
}
}
Is there any way to create new objects with following numbers in the end of the object name?
Thanks!
Edit:
My new code looks like this; I was more thinking like this:
class Persons
{
//Person object id
public int id { get; set; }
//Persons name
public string name { get; set; }
//Persons adress
public string adress { get; set; }
//Persons age
public int age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.Write("How many persons you want to add?: ");
int count = int.Parse(Console.ReadLine());
var newPersons = new List<Persons>(count);
for (int i = 0; i < count; i++)
{
newPersons[i].id = i;
Console.Write("Write name for person " + i);
newPersons[i].name = Console.ReadLine();
Console.Write("Write age for person " + i);
newPersons[i].age = int.Parse(Console.ReadLine());
Console.Write("Write adress for person " + i );
newPersons[i].adress = Console.ReadLine();
}
Console.WriteLine("\nPersons \tName \tAge \tAdress");
for (int i = 0; i < count; i++)
{
Console.WriteLine("\t" + newPersons[i].name + "\t" + newPersons[i].age + "\t" + newPersons[i].adress);
}
Console.ReadKey();
}
}
I understand that I have to create object with arrays or lists. But I didn't really understand how I will access them after they are created person by person..
It's fairly advanced to create dynamic classes (where the actual name of the object is different). In other words, it's WAY harder to do what you're asking than to create a List or an Array. If you spend an hour or two studying Collections, it will pay off in the long run.
Also, you might consider adding a Name property to your Person class, and then you can set that differently for each person you create.
As others have said, you will need to store them in an array or list:
public class Person
{
public string Name { get; set; }
}
static void Main(string[] args)
{
Console.Write("How many persons you want to add?: ");
int p = int.Parse(Console.ReadLine());
var people = new List<Person>();
for (int i = 0; i < p; i++)
{
// Here you can give each person a custom name based on a number
people.Add(new Person { Name = "Person #" + (i + 1) });
}
}
Here's an example of one way to access a Person from the list and let the user update the information. Note that I've added a few properties to the Person class:
public class Person
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public string Address { get; set; }
public int Age
{
// Calculate the person's age based on the current date and their birthday
get
{
int years = DateTime.Today.Year - DateOfBirth.Year;
// If they haven't had the birthday yet, subtract one
if (DateTime.Today.Month < DateOfBirth.Month ||
(DateTime.Today.Month == DateOfBirth.Month &&
DateTime.Today.Day < DateOfBirth.Day))
{
years--;
}
return years;
}
}
}
private static void GenericTester()
{
Console.Write("How many persons you want to add?: ");
string input = Console.ReadLine();
int numPeople = 0;
// Make sure the user enters an integer by using TryParse
while (!int.TryParse(input, out numPeople))
{
Console.Write("Invalid number. How many people do you want to add: ");
input = Console.ReadLine();
}
var people = new List<Person>();
for (int i = 0; i < numPeople; i++)
{
// Here you can give each person a custom name based on a number
people.Add(new Person { Name = "Person" + (i + 1) });
}
Console.WriteLine("Great! We've created {0} people. Their temporary names are:",
numPeople);
people.ForEach(person => Console.WriteLine(person.Name));
Console.WriteLine("Enter the name of the person you want to edit: ");
input = Console.ReadLine();
// Get the name of a person to edit from the user
while (!people.Any(person => person.Name.Equals(input,
StringComparison.OrdinalIgnoreCase)))
{
Console.Write("Sorry, that person doesn't exist. Please try again: ");
input = Console.ReadLine();
}
// Grab a reference to the person the user asked for
Person selectedPerson = people.First(person => person.Name.Equals(input,
StringComparison.OrdinalIgnoreCase));
// Ask for updated information:
Console.Write("Enter a new name (or press enter to keep the default): ");
input = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(input))
{
selectedPerson.Name = input;
}
Console.Write("Enter {0}'s birthday (or press enter to keep the default) " +
"(mm//dd//yy): ", selectedPerson.Name);
input = Console.ReadLine();
DateTime newBirthday = selectedPerson.DateOfBirth;
if (!string.IsNullOrWhiteSpace(input))
{
// Make sure they enter a valid date
while (!DateTime.TryParse(input, out newBirthday) &&
DateTime.Today.Subtract(newBirthday).TotalDays >= 0)
{
Console.Write("You must enter a valid, non-future date. Try again: ");
input = Console.ReadLine();
}
}
selectedPerson.DateOfBirth = newBirthday;
Console.Write("Enter {0}'s address (or press enter to keep the default): ",
selectedPerson.Name);
input = Console.ReadLine();
if (!string.IsNullOrWhiteSpace(input))
{
selectedPerson.Address = input;
}
Console.WriteLine("Thank you! Here is the updated information:");
Console.WriteLine(" - Name ............ {0}", selectedPerson.Name);
Console.WriteLine(" - Address ......... {0}", selectedPerson.Address);
Console.WriteLine(" - Date of Birth ... {0}", selectedPerson.DateOfBirth);
Console.WriteLine(" - Age ............. {0}", selectedPerson.Age);
}
The closest thing to doing what you are asking is to create a dictionary:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var dictionary = new Dictionary<string, Person>();
Console.Write("How many persons you want to add?: ");
int p = int.Parse(Console.ReadLine());
for (int i = 0; i < p; i++)
{
dictionary.Add("NewPerson" + i, new Person());
}
// You can access them like this:
dictionary["NewPerson1"].Name = "Tim Jones";
dictionary["NewPerson2"].Name = "Joe Smith";
}
public class Person
{
public string Name {
get;
set;
}
}
}
You can create dynamically named variables in C#.
What you need is a collection of persons:
var persons = new List<person>();
for (int i = 0; i < p; i++)
{
persons.Add(new person());
}
Arrays and List are basic building blocks. they should't be very hard. but if you don't want to deal with them try creating a method whose responsibility is to give you your new objects given count.
static void Main(string[] args)
{
Console.Write("How many persons you want to add?: ");
int p = int.Parse(Console.ReadLine());
var newPersons = CreatePersons(p);
foreach (var person in newPersons)
{
Console.WriteLine("Eneter age for Person :" person.Name);
person.Age = Console.ReadLine();
}
}
static IEnumerable<Person> CreatePersons(int count)
{
for (int i = 0; i < count; i++)
{
yield return new Person{ Name="newPerson" +1 };
}
}
Try this one.
I am creating Person (Object) as array first (like creating object array)
Then I am assigning that to the Person Class.
class Persons
{
//Person object id
public int id { get; set; }
//Persons name
public string name { get; set; }
//Persons adress
public string adress { get; set; }
//Persons age
public int age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.Write("How many persons you want to add?: ");
int count = int.Parse(Console.ReadLine());
//var newPersons = new List<Persons>(count);
Persons[] newPersons = new Persons[count];
for (int i = 0; i < count; i++)
{
newPersons[i] = new Persons();
newPersons[i].id = i+1;
Console.Write("Write name for person " + (i+1) + "\t");
newPersons[i].name = Console.ReadLine();
Console.Write("Write age for person " + (i + 1) + "\t");
newPersons[i].age = int.Parse(Console.ReadLine());
Console.Write("Write adress for person " + (i + 1) + "\t");
newPersons[i].adress = Console.ReadLine();
}
Console.WriteLine("\nPersons Name \tAge \tAdresss \n");
for (int i = 0; i < count; i++)
{
Console.WriteLine(newPersons[i].name + "\t\t" + newPersons[i].age + "\t" + newPersons[i].adress);
}
Console.ReadKey();
}
}
If you want to iterate through an array of object in C#, you need to define the ToString() method (override) and after that, you can use the ToString() method.

Categories