display array of different objects c# (class inheritance) - c#

I'm having a problem with my code. I'm new to inheritance (only had 1 lecture on it so far) and would appreciate any help here. I've been given this problem and think I have it nearly done. Make a Book class, add two child classes. display all. Plus other stuff but that's the core problem.
When I try to display each object and it's info in a for loop i cant figure out how to access the BookGrade of a TextBook. I have made an array of Books and put everything in there. I know this isn't right but i don't know where to go from here. I have tried making an array of TextBooks like this
TextBook[] bookArray = new TextBook[3] { Book1, TBook1, CBook1};
But then i need to add a cast to Book1, which works, but i still cant implicitly convert type 'CoffeeTableBook' to 'TextBook'. I get this, but don't know how to get around it. I get this error "'Book' does not contain a definition for 'BookGrade'.."
I have 3 classes, Book, TextBook and CoffeeTableBook. Both TextBook and CoffeeTableBook classes are derived from the Book class.
Thanks in advance to anyone who takes the time to help me out.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookDemo
{
class Program
{
static void Main(string[] args)
{
//Book
Book Book1 = new Book();
Book1.BookNum = 123456;
Book1.BookTitle = "Harry Potter 1";
Book1.BookAuthor = "JK Rowling";
Book1.BookPrice = 5.95;
//Text Book
TextBook TBook1 = new TextBook();
TBook1.BookNum = 123436;
TBook1.BookTitle = "Harry Potter 2";
TBook1.BookAuthor = "JK Rowling";
TBook1.BookPrice = 5.95;
TBook1.BookGrade = "A";
//Coffee Table Book
CoffeeTableBook CBook1 = new CoffeeTableBook();
CBook1.BookNum = 123136;
CBook1.BookTitle = "Harry Potter 7";
CBook1.BookAuthor = "JK Rowling";
CBook1.BookPrice = 95.95;
Book[] bookArray = new Book[3] { Book1, TBook1, CBook1 };
for (int i = 0; i < bookArray.Length; i++)
{
if (bookArray[i].GetType() == typeof(TextBook))
{
Console.WriteLine("{0} {1} {2} {3} {4}", bookArray[i].BookNum, bookArray[i].BookTitle, bookArray[i].BookAuthor, bookArray[i].BookPrice, bookArray[i].BookGrade);//'Book' does not contain a defination for 'BookGrade'..
}
else
{
Console.WriteLine("{0} {1} {2} {3} {4}", bookArray[i].BookNum, bookArray[i].BookTitle, bookArray[i].BookAuthor, bookArray[i].BookPrice, "N/A");
}
}
}
}
}
class Book
{
public int BookNum { get; set; }
public string BookTitle { get; set; }
public string BookAuthor { get; set; }
public double BookPrice { get; set; }
}
class TextBook : Book // must be priced between $20.00 and $80.00
{
const double MIN_PRICE = 20;
const double MAX_PRICE = 80;
public string BookGrade { get; set; }
new public double BookPrice
{
set
{
if (value < MIN_PRICE)
{
base.BookPrice = MIN_PRICE;
}
else if (value > MAX_PRICE)
{
base.BookPrice = MAX_PRICE;
}
else
{
base.BookPrice = value;
}
}
get
{
return base.BookPrice;
}
}
}
class CoffeeTableBook : Book //must be priced between $35.00 and $100.00
{
const double MIN_PRICE = 35;
const double MAX_PRICE = 100;
new public double BookPrice
{
set
{
if (value < MIN_PRICE)
{
base.BookPrice = MIN_PRICE;
}
else if (value > MAX_PRICE)
{
base.BookPrice = MAX_PRICE;
}
else
{
base.BookPrice = value;
}
}
get
{
return base.BookPrice;
}
}
}

You need to cast to the specific type Textbook in your if (bookArray[i].GetType() == typeof(TextBook))-Branch
eg.:
if (bookArray[i].GetType() == typeof(TextBook))
{
Console.WriteLine("{0} {1} {2} {3} {4}", bookArray[i].BookNum, bookArray[i].BookTitle, bookArray[i].BookAuthor, bookArray[i].BookPrice, ((TextBook)bookArray[i]).BookGrade); // Works now, because we view bookArray[i] as TextBook and not it's underlaying type
}
else
{
Console.WriteLine("{0} {1} {2} {3} {4}", bookArray[i].BookNum, bookArray[i].BookTitle, bookArray[i].BookAuthor, bookArray[i].BookPrice, "N/A");
}
I'd usually suggest using the new Switch-Capabilities (patterns) described here, but I don't want to interfer with your teacher.

In alternative you can redefine your classes and avoid checking the type in the for:
class Book
{
public int BookNum { get; set; }
public string BookTitle { get; set; }
public string BookAuthor { get; set; }
public double BookPrice { get; set; }
public string BookGrade = "N/A";
}
class TextBook : Book // must be priced between $20.00 and $80.00
{
const double MIN_PRICE = 20;
const double MAX_PRICE = 80;
public string BookGrade { get; set; }
new public double BookPrice
{
set
{
if (value < MIN_PRICE)
{
base.BookPrice = MIN_PRICE;
}
else if (value > MAX_PRICE)
{
base.BookPrice = MAX_PRICE;
}
else
{
base.BookPrice = value;
}
}
get
{
return base.BookPrice;
}
}
}
class CoffeeTableBook : Book //must be priced between $35.00 and $100.00
{
const double MIN_PRICE = 35;
const double MAX_PRICE = 100;
new public double BookPrice
{
set
{
if (value < MIN_PRICE)
{
base.BookPrice = MIN_PRICE;
}
else if (value > MAX_PRICE)
{
base.BookPrice = MAX_PRICE;
}
else
{
base.BookPrice = value;
}
}
get
{
return base.BookPrice;
}
}
}
and in the main:
foreach (Book singleBook in BookArray)
{
Console.WriteLine("{0} {1} {2} {3} {4}", singleBook.BookNum, singleBook.BookTitle, singleBook.BookAuthor, singleBook.BookPrice, singleBook.BookGrade);
}

You can override 'toString()' method to set exact way of displaying your object
class Program
{
class Book
{
public int BookNum { get; set; }
public string BookTitle { get; set; }
public string BookAuthor { get; set; }
public double BookPrice { get; set; }
public override string ToString() //!
{
return string.Format("{0} {1} {2} {3}", this.BookNum, this.BookTitle, this.BookAuthor, this.BookPrice);
}
}
class TextBook : Book // must be priced between $20.00 and $80.00
{
const double MIN_PRICE = 20;
const double MAX_PRICE = 80;
public string BookGrade { get; set; }
new public double BookPrice
{
set
{
if (value < MIN_PRICE)
{
base.BookPrice = MIN_PRICE;
}
else if (value > MAX_PRICE)
{
base.BookPrice = MAX_PRICE;
}
else
{
base.BookPrice = value;
}
}
get { return base.BookPrice; }
}
public override string ToString() //!
{
return base.ToString() + " " + this.BookGrade;
}
}
class CoffeeTableBook : Book //must be priced between $35.00 and $100.00
{
const double MIN_PRICE = 35;
const double MAX_PRICE = 100;
new public double BookPrice
{
set
{
if (value < MIN_PRICE)
{
base.BookPrice = MIN_PRICE;
}
else if (value > MAX_PRICE)
{
base.BookPrice = MAX_PRICE;
}
else
{
base.BookPrice = value;
}
}
get { return base.BookPrice; }
}
}
private static void Main(string[] args)
{
//Book
var book1 = new Book
{
BookNum = 123456,
BookTitle = "Harry Potter 1",
BookAuthor = "JK Rowling",
BookPrice = 5.95
};
//Text Book
var TBook1 = new TextBook
{
BookNum = 123436,
BookTitle = "Harry Potter 2",
BookAuthor = "JK Rowling",
BookPrice = 5.95,
BookGrade = "A"
};
//Coffee Table Book
var CBook1 = new CoffeeTableBook
{
BookNum = 123136,
BookTitle = "Harry Potter 7",
BookAuthor = "JK Rowling",
BookPrice = 95.95
};
var bookArray = new Book[] {book1, TBook1, CBook1};
foreach (Book t in bookArray)
{
Console.WriteLine(t); //! i.e Console.WriteLine(t.ToString());
}
}

You can rewrite your for loop in a shorter format as:
string output;
foreach (Book bk in bookArray)
{
output = string.Format(
"{0} {1} {2} {3} {4}",
bk.BookNum,
bk.BookTitle,
bk.BookAuthor,
bk.BookPrice,
(bk is TextBook)
? (bk as TextBook).BookGrade
: "N/A");
Console.WriteLine(output);
}

Imagine what happens if you add more classes derived from Book, your code will end up getting unwieldy
if(book.GetType() == typeof(TextBook))
{
}
if(book.GetType() == typeof(CoffeeTableBook))
{
}
if(book.GetType() == typeof(AnotherBookType))
{
}
if(book.GetType() == typeof(MagicalSpellsBook))
{
}
if(book.GetType() == typeof(Whatever))
{
}
And it gets even worse if the code to actually display the Books gets into play.
What can you do to solve the issue? First of all introduce a BookRenderer
class BookRenderer
{
List<ISpecificBookRenderer> specificBookRenderers = ...; // pass via constructor
public void RenderBook(Book b)
{
var matchingRenderer = specificBookRenderers.First(r => r.BookType == b.GetType());
matchingRenderer.RenderBook(b);
}
}
Types derived from ISpecificBookRenderer are responsible for rendering your books. The interface ISpecificBookRenderer is defined as
interface ISpecificBookRenderer
{
Type BookType { get; }
void RenderBook(Book b);
}
Now you can create a renderer for each type derived from Book, e.g.
class TextBookRenderer
{
public Type BookType => typeof(TextBook);
public void RenderBook(Book b)
{
if(b is TextBoox textBook)
{
// output TextBook value
}
throw new ArgumentException("Passed book is not a TextBook", nameof(b));
}
}
Of course this comes at costs, but the single classes stay neat and clean. Additionally it's conceivable to need to render your books to a different system. Introducing these renderers allows you to swap implementations, without changing the actual classes. And you don't mix up business logic (your Book classes - and they are in this case, because they perform the price checks) with presentation, which you would do, if you simply overrode ToString to achieve your desired presentation.

Related

calling methods and arrays objects Library Book console app C#

My professor wants us to create a reusable class and console app that lists book objects. I got the first part of the assignment where I am supposed to print the books I created, but now I am stuck on the part where I have to modify the data and print again using the same method and then check out two books and print again. I have tried to look at example online and although some of them have helped, none have been able to get me to pass this roadblock.
class LibraryBook
{
private string _bookTitle;
private string _authorName;
private string _publisher;
private int _copyrightYear;
private string _callNumber;
public LibraryBook(string booktitle, string authorname, string publisher, int ccyear, string callnumber)
{
BookTitle = booktitle;
AuthorName = authorname;
Publisher = publisher;
CopyrightYear = ccyear;
CallNumber = callnumber;
}
public string BookTitle
{
get
{
return _bookTitle;
}
set
{
_bookTitle = value;
}
}
public string AuthorName
{
get
{
return _authorName;
}
set
{
_authorName = value;
}
}
public string Publisher
{
get
{
return _publisher;
}
set
{
_publisher = value;
}
}
public int CopyrightYear
{
get
{
return _copyrightYear;
}
set
{
const int CYEAR = 2019;
if (value > 0)
_copyrightYear = value;
else
_copyrightYear = CYEAR;
}
}
public string CallNumber
{
get
{
return _callNumber;
}
set
{
_callNumber = value;
}
}
public bool Avail;
public void CheckOut()
{
Avail = true;
}
public void ReturnToShelf()
{
Avail = false;
}
public bool IsCheckedOut()
{
return Avail;
}
public override string ToString()
{
return $"Book Title: {BookTitle}{Environment.NewLine}" +
$"Author Name: {AuthorName}{Environment.NewLine}" +
$"Publisher: {Publisher}{Environment.NewLine}" +
$"Copyright Year: {CopyrightYear}{Environment.NewLine}" +
$"Call Number: {CallNumber}{Environment.NewLine}" +
$"Checked Out: {IsCheckedOut()}{Environment.NewLine}";
}
}
}
class Program
{
static void Main(string[] args)
{
LibraryBook[] favBooksArray = new LibraryBook[5];
favBooksArray[0] = new LibraryBook("Harry Potter and the Philospher's Stone", "J.K. Rowling", "Scholastic Corporation", 1997, "HA-12.36");
favBooksArray[1] = new LibraryBook("Harry Potter and the Chamber of Secret", "J.K. Rowling", "Scholastic Corporation", 2001, "HA-13.48");
favBooksArray[2] = new LibraryBook("Tangerine", "Edward Bloor", "Harcourt", 1997, "TB-58.13");
favBooksArray[3] = new LibraryBook("Roll of Thunder, Hear My Cry", "Mildred D. Taylor", "Dial Press", 1976, "RT-15.22");
favBooksArray[4] = new LibraryBook("The Giver", "Lois Lowry", "Fake Publisher", -1, "Fk200-1");
WriteLine($"------LIBRARY BOOKS------{Environment.NewLine}");
BooksToConsole(favBooksArray);
WriteLine($"------CHANGES MADE----- {Environment.NewLine}");
ChangesToBooks(favBooksArray);
BooksToConsole(favBooksArray);
WriteLine($"------RETURNING BOOKS TO SHELF------{Environment.NewLine}");
ReturnBooksToConsole(favBooksArray);
BooksToConsole(favBooksArray);
}
public static void BooksToConsole(LibraryBook[] favBooksArray)
{
foreach (LibraryBook books in favBooksArray)
{
WriteLine($"{books}{Environment.NewLine}");
}
}
public static void ChangesToBooks(LibraryBook[] favBooksArray)
{
favBooksArray[1].AuthorName = "*****The Rock*****";
favBooksArray[3].BookTitle = "****Totally Not A Fake Name*****";
favBooksArray[1].CheckOut();
favBooksArray[4].CheckOut();
}
public static void ReturnBooksToConsole(LibraryBook[] favBooksArray)
{
favBooksArray[1].ReturnToShelf();
favBooksArray[4].ReturnToShelf();
}
}
}

Trying to figure out how to access list in a different method

This is my Code inside my Class. I'm trying to figure out how to access Questions list in DisplayQuestion. I have a program.cs that display a menu for a quiz and I can't have anything static.
public string Question { get; set; }
public List<string> Choices { get; set; }
public char[] CorrectChoice = { 'A', 'B', 'C', 'D' };
public List<string> Questions { get; set; }
These are my methods inside my class. I will need to access this list multiple times inside this class.
public void NewQuestion()
{
Questions = new List<string>();
Choices = new List<string>();
Console.WriteLine("Enter the question: ");
Questions.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 1 for the question:");
Choices.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 2 for the question: ");
Choices.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 3 for the question: ");
Choices.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 4 for the question:");
Choices.Add(Console.ReadLine());
// Console.WriteLine("Enter the correct choice(A,B,C,D)");
foreach (string choice in Choices)
{
Console.WriteLine();
Console.WriteLine(choice);
}
}
public void DisplayQuestions()
{
foreach(string question in Questions)
{
Console.WriteLine();
Console.WriteLine(question);
}
}
try declaring and initializing it to null in the global section or create a class with proper getter setter methods for it.
Don't feel constrained to do everything in one class. Even a rough, verbose attempt at separating concerns into separate classes helps with readability, maintainability, and sanity. Then, you're just a thoughtful refactor away from good code.
You are using C#, an OBJECT-oriented language. So go with the grain and embrace using objects.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Quiz_StackOverflow
{
class Program
{
static void Main(string[] args)
{
var quizGenerator = new QuizGenerator();
var quiz = quizGenerator.GenerateQuiz();
var quizProctor = new QuizProctor();
var grade = quizProctor.ProctorQuiz(quiz);
Console.WriteLine(grade.ToString());
Console.WriteLine("Done. Press any key to exit.");
Console.ReadKey();
}
}
public class QuizGenerator
{
public Quiz GenerateQuiz()
{
var problems = GenerateProblems();
var quiz = new Quiz()
{
Problems = problems
};
return quiz;
}
private List<Problem> GenerateProblems()
{
List<Problem> problems = new List<Problem>();
int numChoices = InputValidator.GetPositiveNumber("Enter number of problems: ");
for (int i = 0; i < numChoices; i++)
{
Problem problem = GenerateProblem();
problems.Add(problem);
}
return problems;
}
private Problem GenerateProblem()
{
var question = GenerateQuestion();
var choices = GenerateChoices();
var answer = GenerateAnswer(choices);
var problem = new Problem()
{
Question = question,
Choices = choices,
Answer = answer
};
return problem;
}
private string GenerateQuestion()
{
Console.WriteLine("Enter the question: ");
string question = Console.ReadLine();
return question;
}
private List<string> GenerateChoices()
{
List<string> choices = new List<string>();
int numChoices = InputValidator.GetPositiveNumber("Enter number of choices for the question: ");
for (int i=1; i<=numChoices; i++)
{
string choice = GenerateChoice(i);
choices.Add(choice);
}
return choices;
}
private string GenerateChoice(int index)
{
Console.WriteLine($"Enter Choice {index} for the question: ");
string choice = Console.ReadLine();
return choice;
}
private Answer GenerateAnswer(List<string> choices)
{
Console.WriteLine("Enter the answer: ");
string userChoice = InputValidator.GetUserChoice(new Problem() { Choices=choices });
var answer = new Answer()
{
Value = userChoice
};
return answer;
}
}
public class QuizProctor
{
public Grade ProctorQuiz(Quiz quiz)
{
var answers = new List<Answer>();
foreach(Problem problem in quiz.Problems)
{
Answer answer = ProctorProblem(problem);
answers.Add(answer);
}
Grade grade = quiz.Grade(answers);
return grade;
}
private Answer ProctorProblem(Problem problem)
{
string userChoice = InputValidator.GetUserChoice(problem);
var answer = new Answer()
{
Value = userChoice
};
return answer;
}
}
public class Quiz
{
public List<Problem> Problems { get; set; }
public Grade Grade(List<Answer> answers)
{
List<Answer> answerKey = Problems.Select(x => x.Answer).ToList();
var rawResults = new List<Tuple<Answer, Answer>>();
for(int i=0; i<answers.Count; i++)
{
Answer correct = answerKey[i];
Answer provided = answers[i];
rawResults.Add(new Tuple<Answer, Answer>(correct, provided));
}
return new Grade(rawResults);
}
}
public class Grade
{
private List<Tuple<Answer, Answer>> RawResults { get; set; }
public decimal Percent
{
get { return decimal.Divide(RawResults.Count(x => x.Item1.Equals(x.Item2)), RawResults.Count); }
}
public Grade(List<Tuple<Answer, Answer>> rawResults)
{
RawResults = rawResults;
}
public override string ToString()
{
return string.Format("You scored a {0:P2}.", Percent);
}
}
public class Problem
{
public string Question { get; set; }
public List<string> Choices { get; set; }
public Answer Answer { get; set; }
public string Prompt()
{
Func<int, char> numberToLetter = (int n) =>
{
return (char)('A' - 1 + n);
};
string prompt = Question;
for (int i=0; i<Choices.Count; i++)
{
string choice = Choices[i];
prompt += $"\n{numberToLetter(i+1)}) {choice}";
}
return prompt;
}
}
public class Answer
{
public string Value { get; set; }
public override string ToString()
{
return Value + "";
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return (obj as Answer).Value.Equals(Value);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
public static class InputValidator
{
public static int GetPositiveNumber(string prompt)
{
int number = -1;
while (number < 0)
{
Console.Write(prompt);
string input = Console.ReadLine();
try
{
number = int.Parse(input);
}
catch (Exception)
{
Console.WriteLine("ERROR: Please input a positive number.");
}
}
return number;
}
public static string GetUserChoice(Problem problem)
{
Func<char, int> letterToNumber = (char c) =>
{
if (char.IsLower(c))
{
return (int)(c - 'a' + 1);
}
return (int)(c - 'A' + 1);
};
char userChoiceLetter = '_';
while (!char.IsLetter(userChoiceLetter))
{
Console.WriteLine(problem.Prompt());
Console.Write("Answer: ");
var input = Console.ReadLine();
try
{
userChoiceLetter = char.Parse(input);
}
catch (Exception)
{
Console.WriteLine("ERROR: Please input a letter corresponding to your choice.");
}
}
int answerIndex = letterToNumber(userChoiceLetter) - 1;
return problem.Choices[answerIndex];
}
}
}

C# - sort List<SomeClass> by using custom CompareTo method

I have the following classes hierarchy:
abstract class Product : IComparable
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Barcode { get; set; }
public int CompareTo(object obj)
{
int ret = -1;
if (String.Compare(this.GetType().Name, obj.GetType().Name,StringComparison.Ordinal) == 0)
ret = 0;
return ret;
}
}
abstract class Book : Product
{
public int PagesCount { get; set; }
}
class ProgrammingBook : Book
{
public string ProgrammingLanguage { get; set; }
}
class CulinaryBook : Book
{
public string MainIngridient { get; set; }
}
class EsotericBook : Book
{
public int MininumAge { get; set; }
}
abstract class Disc : Product
{
internal enum Content
{
Music,
Video,
Software
}
public Content DiscContent { get; set; }
}
class CdDisc : Disc
{
}
class DvdDisc : Disc
{
}
And I trying to sort following collection by using IComparable interface method CompareTo:
List<Product> products = new List<Product>
{
new DvdDisc {Name = "The lord of the rings 2",DiscContent = Disc.Content.Video,Price = 200M,Barcode = "5435443-2"},
new CdDisc {Name = "Antonio Vivaldi: best picks",Price = 700M, DiscContent = Disc.Content.Music,Barcode = "4543765-565"},
new CulinaryBook{Name = "Midterranian foods",MainIngridient = "Salmon",PagesCount = 436,Price = 350M,Barcode = "41457561-897"},
new CdDisc{Name = "Windows XP", DiscContent = Disc.Content.Software, Price = 950M, Barcode = "5433668-4"},
new EsotericBook{Name = "Russian Freemasonry 1731-2000",MininumAge = 21,PagesCount = 2100,Price = 3000M,Barcode = "6464632-876"},
new CdDisc {Name = "The best of Mussorgsky",Price = 300M, DiscContent = Disc.Content.Music,Barcode = "5435436-567"},
new ProgrammingBook{Name = "CLR via C#",PagesCount = 900, Price = 1110M,ProgrammingLanguage = "C#",Barcode = "5546533-2446"},
new DvdDisc {Name = "The lord of the rings 1",DiscContent = Disc.Content.Video,Price = 200M,Barcode = "54354423-2"},
new ProgrammingBook{Name = "ASP.NET MVC 4",PagesCount = 800,Price = 1200M,ProgrammingLanguage = "C#",Barcode = "46476573-65"},
new EsotericBook{Name = "Russian Freemasonry in it's future and past",MininumAge =19, PagesCount = 900, Price = 2342M,Barcode = "3656353-24"},
new CulinaryBook{Name = "Traditional Polish dishes",MainIngridient = "Red meat",PagesCount = 630,Price = 840,Barcode = "54634234-5"}
}; products.Sort();
Output list should look like this:
1.1 Programming books
1.2 Culinary books
1.3 Esoteric books
2.1 Cd discs sorted by content
2.2 DVD discs sorted by content
My current CompareTo method does only part of the job - comparing classes by name.
This is a working example that will render the exact same output:
1.1 Programming books
1.2 Culinary books
1.3 Esoteric books
2.1 Cd discs sorted by content
2.2 DVD discs sorted by content
I also added IComparable<Product> to more easily make it comparable with other products.
abstract class Product : IComparable<Product>
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Barcode { get; set; }
protected abstract int InternalSortOrder { get; }
protected virtual string SortBy { get {return Name;} }
public int CompareTo(Product obj)
{
var sameType = string.Compare(GetType().Name, obj.GetType().Name, StringComparison.Ordinal) == 0;
var sameBaseType = GetType().BaseType != null && obj.GetType().BaseType != null &&
string.Compare(GetType().BaseType.ToString(), obj.GetType().BaseType.ToString(),
StringComparison.Ordinal) == 0;
// They have the same base type, but not the same type. Order by base type first.
if (!sameType && !sameBaseType && GetType().BaseType != null && obj.GetType().BaseType != null)
{
// Order by base type first.
return string.Compare(GetType().BaseType.ToString(), obj.GetType().BaseType.ToString(),
StringComparison.Ordinal);
}
// it's the same base type (eg. book or disc)
if (sameBaseType)
{
// Order by sort order.
if (obj.InternalSortOrder != this.InternalSortOrder)
{
return InternalSortOrder.CompareTo(obj.InternalSortOrder);
}
}
if (sameType)
{
// Same sort order. We sort by name.
return string.Compare(SortBy, obj.SortBy, StringComparison.Ordinal);
}
// Order by Type.
return string.Compare(GetType().Name, obj.GetType().Name, StringComparison.Ordinal);
}
}
abstract class Book : Product
{
public int PagesCount { get; set; }
}
class ProgrammingBook : Book
{
public string ProgrammingLanguage { get; set; }
protected override int InternalSortOrder
{
get { return 1; }
}
}
class CulinaryBook : Book
{
public string MainIngridient { get; set; }
protected override int InternalSortOrder
{
get { return 2; }
}
}
class EsotericBook : Book
{
public int MininumAge { get; set; }
protected override int InternalSortOrder
{
get { return 3; }
}
}
abstract class Disc : Product
{
internal enum Content
{
Music,
Video,
Software
}
protected override string SortBy
{
get { return DiscContent.ToString(); }
}
public Content DiscContent { get; set; }
}
class CdDisc : Disc
{
protected override int InternalSortOrder
{
get { return 1; }
}
}
class DvdDisc : Disc
{
protected override int InternalSortOrder
{
get { return 2; }
}
}
UPDATE:
I've added virtual SortByin Product. It will go to Name by default, but Disc will return Content.
You need to work with equals option
rtn = Primary Sort;
if(rtn =0)
rtn =secondary Sort;
if(rtn =0)
rtn =tertiary Sort;
as your primary sort seems to be on the type of book
you would do
rtn = this.GetType().Name.CompareTo(obj.GetType().Name);
if(rtn ==0)
rtn =this.Name.CompareTo(obj.Name);
return rtn;
if your problem is that you don't want alphabetic sorting on the type
then add to product
public abstract int SortOrder{get;}
and use that as your primary sort

How to Create a simple atm program in c# using inheritance

I'm trying to create an e-ATM console app using C# using inheritance, but every time I debug I see that the derived class values are null, whereas the base class fields or properties are filled with values. Why is the derived class not showing the list with their data even after it is inherited from the base class?
class CreateAccount
{
string firstName, lastName, dateOfBirth, phoneNO, fathersName, mothersName;
double initialBalance; int pinNo = 100, accountNo = 1234, age; DateTime yearOfBirth;
protected static List<CreateAccount> data = new List<CreateAccount>();
protected string FirstName
{
get { return this.firstName; }
set
{
if (string.IsNullOrEmpty(value)) throw new Exception();
else firstName = value;
}
}
protected string LastName
{
get { return this.lastName; }
set
{
if (string.IsNullOrEmpty(value)) throw new Exception();
else lastName = value;
}
}
protected string DateOfBirth
{
get { return this.dateOfBirth; }
set
{
if (string.IsNullOrEmpty(value)) throw new Exception();
else dateOfBirth = value;
}
}
protected string PhoneNo
{
get { return this.phoneNO; }
set
{
if ((string.IsNullOrEmpty(value)) || value.Length != 10)
throw new Exception();
else
phoneNO = value;
}
}
protected string FathersName
{
get { return this.fathersName; }
set
{
if (string.IsNullOrEmpty(value))
throw new Exception();
else
fathersName = value;
}
}
protected string MothersName
{
get { return this.mothersName; }
set
{
if (string.IsNullOrEmpty(value))
throw new Exception();
else
mothersName = value;
}
}
protected double InititailBalance
{
get { return this.initialBalance; }
set
{
if (double.IsNaN(value))
throw new Exception();
else
initialBalance = value;
}
}
protected int PinNo
{
get { return this.pinNo; }
}
protected int AccountNo
{
get { return this.accountNo; }
}
public void GenerateAccount()
{
// code for asking user for their details.
data.Add(this);
}
}
class ATM :CreateAccount
{
public void Deposit()
{
Console.WriteLine("Enter your account number");
int accountNo = int.Parse(Console.ReadLine());
if (accountNo == AccountNo)
{
Console.WriteLine("Enter your amount you wish to deposit");
int amount = int.Parse(Console.ReadLine());
InititailBalance+= amount;
}
}
}
class Program
{
static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Menu");
Console.WriteLine("1.Create Account");
Console.WriteLine("2.ATM");
Console.Write("Please enter your selections: ");
int select = int.Parse(Console.ReadLine());
switch (select)
{
case 1:
CreateAccount account = new CreateAccount();
account.GenerateAccount();
break;
case 2:
ATM atm = new ATM();
atm.Deposit();
break;
}
}
}
}
You are creating two different objects: a 'CreateAccount' Object and an 'ATM' object.
An ATM object does not automatically inherit the values from a previously created CreateAccount object, they are two completely different, unrelated entities.
So for your ATM object to have the same values that your CreateAccount object has, you would have to copy the CreateAccount object to your ATM object.
CreateAccount account = new CreateAccount();
//set account variables here
ATM atm = (ATM)account;
Here is how it's done with proper use of inheritance which is useless in this case actually. Dictionary is the proper datastructure to use in this case because you can avoid duplicates with it. Also from this code you might want to remove the accountNo from Account class to avoid duplicate numbers being kept and the ask it beffore calling GenerateAccount() method. So this is full console app:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ATM
{
class Account
{
string firstName, lastName, dateOfBirth, phoneNO, fathersName, mothersName;
double initialBalance;
int pinNo, accountNo, age;
DateTime yearOfBirth;
public Account()
{
pinNo = 100;
accountNo = 1234;
}
public string FirstName
{
get { return this.firstName; }
set
{
if (string.IsNullOrEmpty(value)) throw new Exception();
else firstName = value;
}
}
public string LastName
{
get { return this.lastName; }
set
{
if (string.IsNullOrEmpty(value)) throw new Exception();
else lastName = value;
}
}
public string DateOfBirth
{
get { return this.dateOfBirth; }
set
{
if (string.IsNullOrEmpty(value)) throw new Exception();
else dateOfBirth = value;
}
}
public string PhoneNo
{
get { return this.phoneNO; }
set
{
if ((string.IsNullOrEmpty(value)) || value.Length != 10)
throw new Exception();
else
phoneNO = value;
}
}
public string FathersName
{
get { return this.fathersName; }
set
{
if (string.IsNullOrEmpty(value))
throw new Exception();
else
fathersName = value;
}
}
public string MothersName
{
get { return this.mothersName; }
set
{
if (string.IsNullOrEmpty(value))
throw new Exception();
else
mothersName = value;
}
}
public double InititailBalance
{
get { return this.initialBalance; }
set
{
if (double.IsNaN(value))
throw new Exception();
else
initialBalance = value;
}
}
public int PinNo
{
get { return this.pinNo; }
}
public int AccountNo
{
get { return this.accountNo; }
}
public void GenerateAccount()
{
// code for asking user for their details.
}
}
class ATM
{
public static Dictionary<int, Account> AccountsList;
static ATM()
{
AccountsList = new Dictionary<int, Account>();
}
public void CreateAccount()
{
Account acc = new Account();
acc.GenerateAccount();
AccountsList.Add(acc.AccountNo, acc);
}
public void Deposit()
{
Console.WriteLine("Enter your account number");
int accountNo = int.Parse(Console.ReadLine());
if (AccountsList.ContainsKey(accountNo))
{
Console.WriteLine("Enter your amount you wish to deposit");
int amount = int.Parse(Console.ReadLine());
AccountsList[accountNo].InititailBalance += amount;
}
}
}
class Program
{
static void Main(string[] args)
{
ATM atm = new ATM();
while (true)
{
Console.WriteLine("Menu");
Console.WriteLine("1.Create Account");
Console.WriteLine("2.ATM");
Console.Write("Please enter your selections: ");
int select = int.Parse(Console.ReadLine());
switch (select)
{
case 1:
atm.CreateAccount();
break;
case 2:
atm.Deposit();
break;
default:
Console.WriteLine("Invalid selection!");
break;
}
}
}
}
}
CreateAccount is an operation of Atm and this is why I don't think you should be using inheritance. I propose this solution:
Class Account:
class Account
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PhoneNumber { get; set; }
public double Balance { get; set; }
// More properties here
...
}
Class Atm:
class Atm
{
public List<Account> Accounts { get; set; }
public Atm()
{
Accounts = new List<Account>();
}
public void CreateAccount()
{
var account = new Account();
// Get details from user here:
...
account.Balance = 0.0;
account.Id = Accounts.Count + 1;
Accounts.Add(account);
}
public void Deposit()
{
int accountId;
// Get input from the user here:
// --------------------------------
// 1. Check that the account exists
// 2. Deposit into the account.
...
}
Full example:
class Program
{
static void Main()
{
var atm = new Atm();
while (true)
{
int option;
Console.WriteLine();
Console.WriteLine("Menu:");
Console.WriteLine("1. Create Account");
Console.WriteLine("2. Deposit");
Console.WriteLine();
Console.Write("Please make a selection: ");
var input = int.TryParse(Console.ReadLine(), out option);
Console.WriteLine("-----------------");
switch (option)
{
case 1:
atm.CreateAccount();
break;
case 2:
atm.Deposit();
break;
}
}
}
}
class Account
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PhoneNumber { get; set; }
public double Balance { get; set; }
}
class Atm
{
public List<Account> Accounts { get; set; }
public Atm()
{
Accounts = new List<Account>();
}
public void CreateAccount()
{
var account = new Account();
Console.WriteLine("Create a new account!");
Console.WriteLine();
Console.Write("Enter first name: ");
account.FirstName = Console.ReadLine();
Console.Write("Enter last name: ");
account.LastName = Console.ReadLine();
Console.Write("Enter date of birth: ");
account.DateOfBirth = DateTime.Parse(Console.ReadLine());
Console.Write("Enter phone number: ");
account.PhoneNumber = Console.ReadLine();
account.Balance = 0.0;
account.Id = Accounts.Count + 1;
Accounts.Add(account);
}
public void Deposit()
{
int accountId;
Console.Write("Enter your account number: ");
int.TryParse(Console.ReadLine(), out accountId);
var account = Accounts.FirstOrDefault(a => a.Id == accountId);
if (account != null)
{
double amount;
Console.Write("Enter amount to deposit: ");
double.TryParse(Console.ReadLine(), out amount);
account.Balance += amount;
Console.Write("Your new balance is {0}", account.Balance);
}
else
{
Console.WriteLine("That account does not exist!");
}
}
}

C# .Net 3.5 Using Overloaded Indexers with different return types

I have a parent class which is essentially a glorified list. It's extended by several subclasses for various functionalities.
public class HierarchialItemList<ItemType> : IEnumerable<ItemType>
{
public ItemType this[String itemCode]
{
get
{
foreach (IHierarchialItem curItem in hierarchialItems)
{
if (curItem.Code.Equals(itemCode, StringComparison.CurrentCultureIgnoreCase))
{
return ((ItemType)curItem);
}
}
return (default(ItemType));
}
}
public ItemType this[Int32 index]
{
get
{
return (hierarchialItems[index]);
}
}
}
public class DatabaseList : HierarchialItemList<Database>
{
public DatabaseList this[CommonDatabaseType typeToFilter]
{
get
{
DatabaseList returnList = new DatabaseList();
foreach(Database curDatabase in this)
{
if (curDatabase.DatabaseType.Equals(typeToFilter))
{
returnList.Add(curDatabase);
}
}
return (returnList);
}
}
public DatabaseList this[Environments.RMSEnvironment environmentToFilter]
{
get
{
DatabaseList returnList = new DatabaseList();
foreach(Database curDatabase in this)
{
if (curDatabase.ParentEnvironment.Equals(environmentToFilter))
{
returnList.Add(curDatabase);
}
}
return (returnList);
}
}
}
The problem is that C# thinks that this:
Database testDatabase = sampleDatabaseList[0];
Is an error and that the indexer should be returning a DatabaseList, not a Database. You and I both know that's false. Any workarounds or do all indexers have to have the same return type?
Edit: I just figured out that it's because of using an enumeration as an indexer which is internally an integer. Still, any way to use both an enumeration and an integer?
Edit 2: As requested, here is some compiliable test code which demonstrates the problem.
using System;
using System.Collections.Generic;
namespace CSQT
{
class A<T>
{
List<T> temp;
public A()
{
temp = new List<T>();
}
public void AddItem(T itemToAdd)
{
temp.Add(itemToAdd);
}
public T this[String code]
{
get { return (temp[0]); }
}
public T this[Int32 index]
{
get { return (temp[index]); }
}
}
class B : A<String>
{
public B()
: base()
{
}
public B this[BTypes testType]
{
get
{
return (this);
}
}
}
enum BTypes { TEMP1, TEMP2 };
class C
{
public C()
{
B temp = new B();
//Compile error: Cannot implicitly convert type 'CSQT.B' to 'string'
String temp2 = temp[0];
//Compile error: Cannot convert type 'CSQT.B' to 'string'
String temp3 = (String)temp[0];
//This compiles and runs but instead of going to A.this[int32], it goes to B.this[BTypes testType]
B temp4 = temp[0];
}
}
}
Why do we know that to be false? The line
Database testDatabase = sampleDatabaseList[0];
invokes the indexer with the parameter 0 which is a int literal and therefore, seeing that DatabaseList inherits from HierarchialItemList<Database> will invoke the indexer defined by
public ItemType this[Int32 itemCode] { get; }
which is declared to return an ItemType. You haven't told us what ItemType is. We have no reason to know that an ItemType can be assigned to a variable of type Database.
Indexers do not have to return the same type. However, it is not possible to overload solely on the basis of return type. That is, this is legal
class IndexerTest {
public int this[int index] {
get {
return 0;
}
}
public string this[double index] {
get {
return "Hello, success!";
}
}
}
This is not
class IndexerTest {
public int this[int index] {
get {
return 0;
}
}
public string this[int index] {
get {
return "Hello, fail!";
}
}
}
Responding to your edit:
Edit: I just figured out that it's because of using an enumeration as an indexer which is internally an integer. Still, any way to use both an enumeration and an integer?
If you want to invoke the indexer that accepts an enum, invoke it like so:
sampleDatabaseList[Environments.RMSEnvironment.SomeEnumValue];
This is perfectly valid code.
class SomeClass { }
public class A<T> : IEnumerable<T>
{
public T this[int index]
{
get
{
return (this[index]);
}
}
public T this[String index]
{
get
{
return (this[index]);
}
}
}
public class B : A<SomeClass>
{
public B this[char typeToFilter]
{
get
{
return new B();
}
}
}
B classList = new B();
SomeClass test = classList[0];
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestNameSpace
{
public class Employee : Person
{
string id;
public string Id
{
get { return id; }
set { id = value; }
}
decimal salary;
public decimal Salary
{
get { return salary; }
set { salary = value; }
}
string password;
public string Password
{
set { password = value; }
}
int ichk = 1, schk = 1, pchk = 1;
public Employee()
: base()
{
Id = null;
Salary = Convert.ToDecimal("0.0");
Password = null;
}
public Employee(string n, char g, DateTime d, string empid, decimal sal, string pwd)
: base(n, g, d)
{
Id = empid;
Salary = sal;
Password = pwd;
}
public override void Accept()
{
base.Accept();
try
{
Console.Write("Enter the EMPID:");
Id = Console.ReadLine();
if (string.IsNullOrEmpty(Id) == true)
{
ichk = 0;
Console.WriteLine("No ID entered!");
}
string salcheck;
Console.Write("Enter the Salary:");
salcheck = Console.ReadLine();
if (string.IsNullOrEmpty(salcheck) == true)
{
schk = 0;
Console.WriteLine("Invalid Salary");
}
else
{
Salary = Convert.ToDecimal(salcheck);
if (Salary < 0)
{
schk = 0;
Console.WriteLine("Invalid Salary");
}
}
Console.Write("Enter Password:");
Password = Console.ReadLine();
if (string.IsNullOrEmpty(password) == true)
{
pchk = 0;
Console.WriteLine("Empty Password!");
}
else
{
string pwd;
int pchk = 0;
do
{
Console.Write("Re-Enter Password:");
pwd = Console.ReadLine();
if (string.IsNullOrEmpty(pwd) == true || pwd != password)
Console.WriteLine("Passwords don't match!");
else
pchk = 1;
} while (pchk == 0);
}
}
catch (Exception e)
{
Console.Write(e.Message);
}
}
public override void Print()
{
base.Print();
if (ichk == 1)
{
Console.WriteLine("EMPID:{0}", Id);
}
else
Console.WriteLine("No Id!");
if (schk == 1)
{
Console.WriteLine("Salary:{0}", Salary);
}
else
Console.WriteLine("Invalid Salary!");
}
}
}
------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestNameSpace
{
public class Person
{
protected string name;
public string Name
{
get { return name; }
set { name = value; }
}
protected char gender;
public char Gender
{
get { return gender; }
set { gender = value; }
}
protected DateTime? dob;
public DateTime? Dob
{
get { return dob; }
set { dob = value; }
}
protected int age;
public int Age
{
get { return age; }
}
public Person()
{
Name = "Default";
Gender = 'M';
Dob = null;
age = 0;
}
public Person(string n, char g, DateTime d)
{
Name = n;
Gender = g;
Dob = d;
age = DateTime.Now.Year - Dob.Value.Year;
}
int nchk = 1, gchk = 0, dchk = 0;
string datetimecheck, gendercheck;
public virtual void Accept()
{
try
{
Console.Write("Enter the name:");
Name = Console.ReadLine();
if (string.IsNullOrEmpty(Name)==true)
{
nchk = 0;
Console.WriteLine("No name entered!");
}
Console.Write("Enter the Date of birth:");
datetimecheck = Console.ReadLine();
if (string.IsNullOrEmpty(datetimecheck) == true)
{
dchk = 0;
Console.WriteLine("No date entered!");
}
else
{
Dob = Convert.ToDateTime(datetimecheck);
age = DateTime.Now.Year - Dob.Value.Year;
dchk = 1;
}
Console.Write("Enter Gender:");
gendercheck = Console.ReadLine();
if (gendercheck != "m" && gendercheck != "M" && gendercheck != "f" && gendercheck != "F")
{
gchk = 0;
Console.WriteLine("Invalid Gender");
}
else
{
gchk = 1;
Gender = Convert.ToChar(gendercheck);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public virtual void Print()
{
Console.WriteLine("\n\nThe Employee Details are:\n");
if (nchk == 1)
{
Console.WriteLine("Name:{0}", Name);
}
else
Console.WriteLine("No Name!");
if (gchk == 1)
{
Console.WriteLine("Gender:{0}", Gender);
}
else
Console.WriteLine("No Gender!");
if (dchk == 1)
{
Console.WriteLine("Date Of Birth:{0}", Dob);
Console.WriteLine("Age:{0}", Age);
}
else
Console.WriteLine("No Date Of Birth!");
}
}
}
After adding the necessary classes and attributes to get your code sample to compile, I was able to run this statement with no issues:
Database testDatabase = sampleDatabaseList[0];
If you're getting an error that sampleDatabaseList[0] returns a DatabaseList, please provide a compilable code sample that contains the statement DatabaseList testDatabase = sampleDatabaseList[0];
---TRIGGER--
CREATE TRIGGER TriggerTest
ON EMP
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
declare #day varchar(10)
select #day=datename(dw,getdate())
declare #hour int
Select #hour=convert(varchar(2),getdate(),114)
if ( #hour < 9 OR #hour > 13 OR #day = 'Saturday' OR #day = 'Sunday')
BEGIN
if UPDATE(EMPID)
RAISERROR ('Error!',1,16)
rollback tran
END
END
Insert into EMP values(1003,'A','A')
drop trigger TriggerTest
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestNameSpace
{
public class Employee:Person
{
string id;
public string Id
{
get { return id; }
set { id = value; }
}
decimal salary;
public decimal Salary
{
get { return salary; }
set { salary = value; }
}
string password;
public string Password
{
set { password = value; }
}
int ichk = 1, schk = 1, pchk = 1;
public Employee()
: base()
{
Id = null;
Salary = Convert.ToDecimal("0.0");
Password = null;
}
public Employee(string n, char g, DateTime d, string empid, decimal sal, string pwd)
: base(n, g, d)
{
Id = empid;
Salary = sal;
Password = pwd;
}
public override void Accept()
{
base.Accept();
try
{
Console.Write("Enter the EMPID:");
Id = Console.ReadLine();
if (Id == null)
{
ichk = 0;
Console.WriteLine("No ID entered!");
}
Console.Write("Enter the Salary:");
Salary = Convert.ToDecimal(Console.ReadLine());
if (Salary < 0)
{
schk = 0;
Console.WriteLine("Invalid Salary");
}
Console.Write("Enter Password:");
Password = Convert.ToString(Console.ReadLine());
if (password == null)
{
pchk = 0;
Console.WriteLine("Empty Password!");
}
}
catch (Exception e)
{
Console.Write(e.Message);
}
}
public override void Print()
{
base.Print();
if (ichk == 1)
{
Console.WriteLine("EMPID:{0}", Id);
}
else
Console.WriteLine("No Id!");
if (schk == 1)
{
Console.WriteLine("Salary:{0}", Salary);
}
else
Console.WriteLine("Invalid Salary!");
}
}
}
-----PERSON-----
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestNameSpace
{
public class Person
{
protected string name;
public string Name
{
get { return name; }
set { name = value; }
}
protected char gender;
public char Gender
{
get { return gender; }
set { gender = value; }
}
protected DateTime dob;
public DateTime Dob
{
get { return dob; }
set { dob = value; }
}
protected int age;
public int Age
{
get { return age; }
}
public Person()
{
Name = "Default";
Gender = 'M';
Dob = Convert.ToDateTime("09 / 12 / 1990");
age = 0;
}
public Person(string n, char g, DateTime d)
{
Name = n;
Gender = g;
Dob = d;
age = DateTime.Now.Year - Dob.Year;
}
int nchk = 1, gchk = 1, dchk = 1;
public virtual void Accept()
{
try
{
Console.Write("Enter the name:");
Name = Console.ReadLine();
if(Name == null)
{
nchk = 0;
Console.WriteLine("No name entered!");
}
Console.Write("Enter the Date of birth:");
Dob = Convert.ToDateTime(Console.ReadLine());
if (Dob == null)
{
dchk = 0;
Console.WriteLine("No date entered!");
}
else
{
age = DateTime.Now.Year - Dob.Year;
}
Console.Write("Enter Gender:");
Gender = Convert.ToChar(Console.ReadLine());
if (Gender != 'm' && Gender != 'M' && Gender != 'F' && Gender != 'f')
{
gchk = 0;
Console.WriteLine("Invalid Gender");
}
}
catch (Exception e)
{
Console.Write(e.Message);
}
}
public virtual void Print()
{
Console.WriteLine("\n\nThe Employee Details are:\n");
if (nchk == 1)
{
Console.WriteLine("Name:{0}", Name);
}
else
Console.WriteLine("No Name!");
if (gchk == 1)
{
Console.WriteLine("Gender:{0}", Gender);
}
else
Console.WriteLine("No Gender!");
if (dchk == 1)
{
Console.WriteLine("Date Of Birth:{0}", Dob);
Console.WriteLine("Age:{0}", Age);
}
else
Console.WriteLine("No Date Of Birth!");
}
}
}

Categories