Converting objects to Subclass - c#

I want to make a class Vehicle and two classes (PassengerVehicle and FreightVehicle) that inherit it. The problem is that when the user enters the type of vehicle he wants and when I convert an object from Vehicle to the desired type, I can't use those class methods later. Here is my code, how can I fix this?
using System;
namespace Vehicle_Primer
{
enum FuelType
{
Gas,
Diesel
}
class Vehicle
{
private FuelType FuelType { get; set; }
}
class PassengerVehicle : Vehicle
{
private int SeatNumber { get; set; }
private int PassengerNumber { get; set; }
public void CheckSeats()
{
if (PassengerNumber > SeatNumber) Console.WriteLine("Not enough seats");
else Console.WriteLine("Enough seats");
}
}
class FreightVehicle : Vehicle
{
private int Capacity { get; set; }
private int Mass { get; set; }
public void CheckCapacity()
{
if (Mass > Capacity) Console.WriteLine("Load capacity exceeded");
else Console.WriteLine("Load capacity not exceeded");
}
}
internal class Program
{
static void Main()
{
Vehicle vehicle = null;
while (true)
{
Console.WriteLine("Enter vehicle type");
string input = Console.ReadLine();
if (input == "passenger")
{
vehicle = new PassengerVehicle();
break;
}
else if (input == "freight")
{
vehicle = new FreightVehicle();
break;
}
Console.WriteLine("Wrong input");
}
if (vehicle is FreightVehicle)
{
vehicle.CheckCapacity();
}
else
{
vehicle.CheckSeats();
}
}
}
}

I think the problem here is you are not using properly pattern matching. When using pattern matching you need to declare a variable to test against, like as follow:
if (vehicle is FreightVehicle freight)
{
freight.CheckCapacity();
}
else if (vehicle is PassengerVehicle passenger)
{
passenger.CheckSeats();
}
In other words, when using pattern matching, it implicitly forces you to declare the variable to test, and if the type is correct you can use that object and its methods and properties.
For a more detailed expalanation, you can see this link at Type Tests.

Another option is to restructure your program to use the concrete type directly, although this mixes IO and non-IO a bit:
static void Main()
{
Vehicle vehicle = null;
while (true)
{
Console.WriteLine("Enter vehicle type");
string input = Console.ReadLine();
if (input == "passenger")
{
PassengerVehicle vehicle = new PassengerVehicle();
vehicle.CheckSeats();
break;
}
else if (input == "freight")
{
FreightVehicle vehicle = new FreightVehicle();
vehicle.CheckCapacity();
break;
}
Console.WriteLine("Wrong input");
}
}

Related

How could I iterate through list of abstract type, containing non-abstract types derived from that abstract class?

I need to iterate through list I created, but can't access objects inside. I tried a few different functions but nothing worked and I'm afraid I'm using the wrong tools for the job.
namespace WholesaleApp
{
internal class Program : Wholesale
{
static string filePath = "C:\\Wholesale.txt";
static void Main(string[] args)
{
List<Merchandise> productList = new List<Merchandise>();
productList = ReadFromFile(filePath);
foreach(Merchandise merchandise in productList)
{
//this is where I'm trying to access objects inside and display them
}
}
}
}
This is my abstract class:
namespace WholesaleApp
{
internal abstract class Merchandise
{
public string merchandiseName { get; set; }
public int merchandiseAmount { get; set; }
public Merchandise(string name, int amount)
{
merchandiseName = name;
merchandiseAmount = amount;
}
}
}
And this is one of the three classes deriving from Merchandise abstract class:
namespace WholesaleApp
{
internal class MerchandiseClothing : Merchandise
{
public string clothSize { get; set; }
public string clothType { get; set; }
public MerchandiseClothing(string _clothType, string _clothSize, string name, int amount) : base(name, amount)
{
clothType = _clothType;
clothSize = _clothSize;
}
public void ReturnAll()
{
Console.Write(merchandiseName+" of type: "+clothSize+" in amount: "+merchandiseAmount+ " and jeep status is: "+clothType);
}
}
}
Finally, my function where I add everything to the final list:
namespace WholesaleApp
{
internal class Wholesale
{
static public List<Merchandise> ReadFromFile(string filePath)
{
List<Merchandise> result = new List<Merchandise>();
string line;
StreamReader reader = null!;
try
{
reader = new StreamReader(filePath);
line = reader.ReadLine()!;
while (line != null)
{
string[] words = line.Split(';');
if (words[0] == "MerchandiseComputer")
{
result.Add(new MerchandiseComputer(words[1], words[2], Int32.Parse(words[3])));
}
else if (words[0] == "MerchandiseCarParts")
{
result.Add(new MerchandiseCarParts(bool.Parse(words[1]), words[3], words[2], Int32.Parse(words[4])));
}
else if (words[0] == "MerchandiseClothing")
{
result.Add(new MerchandiseClothing(words[1], words[2], words[3], Int32.Parse(words[4])));
}
line = reader.ReadLine()!;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
reader.Close();
}
return result;
}
}
}
It should be possible to iterate here iterate through objects already. If you want to use specific fields from each specific class, you can put here a check on type and do whatever you want. For example:
foreach (Merchandise merchandise in productList)
{
if (merchandise is MerchandiseClothing clothing)
{
Console.WriteLine(clothing.clothSize); //Can be use any field from Clothing class
Console.WriteLine(clothing.merchandiseAmount); //And also from parent
}
else if (merchandise is MerchandiseComputer computer)
{
//Do what you want
}
}
However, better to make abstract method like WriteToConsole in Merchandise class and override it in each implementation. Like ReturnAll method in your MerchandiseClothing class

Can property setter condition acitvate by constructor?

I was expected that constructor can go into the setter condition, I have done the following attempts.
static void Main(string[] args)
{
Iitem car = new Car(7000);
Console.WriteLine($"cost={car.cost}");//expect output "too expensive",but actually show 7000
car.cost = 7000;
Console.ReadLine();
}
public interface Iitem
{
int cost { get; set; }
string status {get;set; }
}
class Car:Iitem
{
private int mycost;
public int cost
{
get { return mycost; }
set
{
if (value > 5000)
{
mycost = 0;
Console.WriteLine("too expensive");
}
else
{
mycost = value;
}
}
}
public string status { get; set; }
public Car(int cost)
{
this.mycost = cost;
}
}
If I discard car.cost=7000 from Main() function, then I can't get the output of too expensive.
you are not getting the desired result because you are setting the value directly into the variable "mycost" in the constructor. Replace it with this.cost = cost;

Is there a way to access T.Method() in a new instance of T in a generic method?

I have a generic method
class Program {
Character CreateChar<T>() where T : new() {
T DChar = new T();
Character Char = new Character {
Name = DChar.Name,
Health = DChar.Health
};
return Char;
}
public static void Main() {
Character Char1 = CreateChar<Mage>();
}
}
Where I have a Character class, and that has the basic stuff like Name, Health, ect.
class Character {
public string Name { get; set; }
public int Health { get; set; }
}
But (this is like a game) I have some set types like "Mage".
class Mage {
public int Health {
get {
return 5;
}
}
}
So my idea was to make a generic method "CreateChar" so I can pass Mage to it like so in Main:
Character Char1 = CreateChar<Mage>();
However I can't seem to access DChar.Name or DChar.Health since "T does not contain a definition for 'Health'". So, is there a way to access the T method? Or is there just a better way of handling this?
In fact, now that I see it, "CreateChar();" is invalid as well because "an object reference is required for a nonstatic field". So I guess my question is, what is wrong, and how do I fix it?
In a generic method, you can only access the methods defined by the constraint on T (unless you use reflection or type checking/casting, both of which defeat the purpose of generics). If you constrain T to a Character (which I think you have to do to access Name and Health), then it should work (please post the Character class with relevant properties). But then in order to pass it a Mage, you'd have to have Mage inherit from Character.
Here's what I mean; first create an interface that defines properties that all characters will have (i.e. all public properties, methods, and events):
public interface ICharacter
{
string Name { get; set; }
string Description { get; }
int Health { get; set; }
}
Then we can create a base class called Character that implements the interface::
public class Character : ICharacter
{
public string Name { get; set; } = "Default Character";
public int Health { get; set; } = 5;
public string Description { get; protected set; } = "Default Description";
}
Next we create some character types that inherit the Character class:
public class Mage : Character
{
public Mage()
{
Name = "Default Mage";
Description = "Someone who uses or practices magic " +
"derived from supernatural or occult sources.";
}
}
public class Elf : Character
{
public Elf()
{
Name = "Default Elf";
Description = "A supernatural creature of folk tales, " +
"typically represented as a small, elusive figure " +
"in human form with pointed ears, magical powers, " +
"and a capricious nature.";
}
}
So now we can constrian our generic type T to the ICharacter interface, and we can access the Name and Health properties:
class Program
{
T CreateChar<T>() where T : ICharacter, new()
{
var result = new T();
result.Name += " (created in 'CreateChar' method)"; // Modify a property
return result;
}
// Rest of class omitted
}
You need to constrain T to an interface or a base class that contains the method you want to call.
I think you are looking at a factory pattern here. Does this help?
public class Character
{
protected Character(string name, int health)
{
Name=name;
Health=health;
}
public string Name { get; set; }
public int Health { get; set; }
}
public class Mage : Character
{
public Mage() : base("Evil Mage", 5)
{
this.Mana = 10;
}
public int Mana { get; set; }
}
public class Paladin : Character
{
public Paladin() : base("Holy Paladin", 8)
{
this.Shield = 2;
}
public int Shield { get; set; }
}
public static class ChatacterFactory
{
public static TChar Create<TChar>(string name) where TChar : Character, new()
{
var result = new TChar();
result.Name = name;
return result;
}
}
class Program
{
static void Main(string[] args)
{
Mage mage = ChatacterFactory.Create<Mage>("Boom Mage");
Paladin paladin = ChatacterFactory.Create<Paladin>("White Knight");
}
}
if I understood you right one of these should work for you:
//case 1
class Character1
{
static Character1 CreateChar<T>(T character) where T : CommonGameCharecterClassOrInterface, new()
{
Character1 achar = new Character1
{
Name = character.Name,
Health = character.Health
};
return achar;
}
}
class Character2
{
static Character2 CreateChar(dynamic character)
{
Character2 achar = new Character2
{
Name = character.Name,
Health = character.Health
};
return achar;
}
}
//case 2
class Character3
{
static Character3 CreateChar<T>() where T : CommonGameCharecterClassOrInterface, new()
{
T character = new T();
Character3 achar = new Character3
{
Name = character.Name,
Health = character.Health
};
return achar;
}
}
class Character4
{
static Character4 CreateChar<T>() where T : new()
{
dynamic character = new T();
Character4 achar = new Character4
{
Name = character.Name,
Health = character.Health
};
return achar;
}
}

C# limit options of enum in a derived class

So I'm trying to limit the options ,of an enum I declared in the base class, I can select in the derived class.
namespace FLYNET.Personeel
{
public enum Graad
{
Captain,
SeniorFlightOfficer,
SecondOfficer,
JuniorFlightOfficer,
Steward,
Purser
};
public abstract class VliegendPersoneelslid : Personeelslid
{
public VliegendPersoneelslid()
{
}
public override decimal BerekenTotaleKostprijsPerDag()
{
return BasisKostprijsPerDag;
}
public List<Certificaat> Certificaten { get; set; }
}
The above is the enum I'm trying to use from the base class Staff.
In the derived class Cabincrew I want to limit the options that can be selected in the constructor. If the wrong option is selected, it needs to throw a specified exception, so a bit like this :
namespace FLYNET.Personeel
{
public class CockpitPersoneel : VliegendPersoneelslid
{
public int VliegUren { get; set; }
public Graad Graad { get; set; }
public CockpitPersoneel()
{
if (Grade = Graad.Steward || Grade = Graad.Purser)
{
throw new Exception
}
}
public override decimal BerekenTotaleKostprijsPerDag()
{
decimal kostprijsPersoneel = 0m;
decimal percentage;
return kostprijsPersoneel;
}
}
}
I'm aware this is probably a beginners question (and it is :p) but please bear with me.
I suggest using extension methods in order to hide options
public enum Grade {
None, // <- zero option is often a good idea to include
Captain,
SeniorFlightOfficer,
SecondOfficer,
JuniorFlightOfficer,
Steward,
Purser };
public static class GradeExtensions {
public static bool IsCabinCrue(this Grade grade) {
return grade == Grade.Steward || grade == Grade.Purser;
}
public static bool IsCockpitPersonnel(this Grade grade) {
return grade == Grade.Captain ||
grade == Grade.SeniorFlightOfficer ||
grade == Grade.SecondOfficer ||
grade == Grade.JuniorFlightOfficer;
}
}
Then you can use extension methods as if their are methods of the enum when validating Grade values provided:
public class CabinCrue {
...
public CabinCrue(Grade grade) {
// Validation: Cabin Crue grade only
// not Exception but ArgumentException: it's argument grade that's wrong
if (!grade.IsCabinCrue())
throw new ArgumentException("Cabin crue only", "grade");
Grade = grade;
...
}
public Grade Grade {
get;
private set;
}
...
}
I think you have an error in logic. You are trying to verify the Grade or Graad in the constructor. The property Graad cannot be set before the constructor call. Your validation will check the default value of the variable Graad (which in your case will be Captain).
You would need to pass the Graad into the constructor to be able to make that validation:
public CockpitPersoneel(Graad g)
{
if (g = Graad.Steward || g = Graad.Purser)
{
throw new Exception("wrong choice");
}
else
{
this.Graad = g;
}
}
What you also could do is to put the Validation logic directly into the full property and throw there the exception:
Graad _myGrade;
public Graad GradeOfPerson
{
get { return _myGrade; }
set
{
if (value = Graad.Steward || value = Graad.Purser)
{
throw new Exception("Not Allowed");
}
_myGrade = value;
}
}
this way you can leave your constructor blank:
public CockpitPersoneel()
{
}

instance access to class members of inherited abstract

This is theory Thursday I guess.
Shouldn't Main() have access to _XLocal & _YLocal?
using System;
namespace HelloGoodbyeOperator {
public abstract class HGOperator {
public string _greeting { get; set; }
public bool _x { get; internal set; }
public bool _y { get; internal set; }
public static implicit operator HGOperator(bool mode) {
object ret = new object();
if (mode)
ret = new HGOperator_Hello { _greeting = "hello", _XLocal = 10 };
else
ret = new HGOperator_Goodbye { _greeting = "goodbye", _YLocal = 20 };
return (HGOperator)ret;
}
}
public class HGOperator_Hello : HGOperator {
public int _XLocal { get; set; }
public HGOperator_Hello() { _x = true; Console.WriteLine("HGOperator_Hello //" + _XLocal.ToString() + "\\\\"); }
}
public class HGOperator_Goodbye : HGOperator {
public int _YLocal { get; set; }
public HGOperator_Goodbye() { _y = false; Console.WriteLine("HGOperator_Goodbye //", _YLocal, "\\\\"); }
}
class Program {
static void Main(string[] args) {
HGOperator hg = true;
Console.WriteLine(hg._greeting);
test(hg);
Console.WriteLine("");
hg = false;
Console.WriteLine(hg._greeting);
test(hg);
Console.ReadKey();
}
static void test(HGOperator hg) {
if (hg is HGOperator_Hello) {
Console.WriteLine(hg._x);
//Console.WriteLine(hg._XLocal);
} else {
Console.WriteLine(hg._y);
//Console.WriteLine(hg._YLocal);
}
}
}
}
Here is the output
HGOperator_Hello //0\
hello
True
HGOperator_Goodbye //
goodbye
False
I can understand how trying to access hg._YLocal of a HGOperator_Hello type would be a nightmare & vise-versa. But would still think I could get to the respective members with caution.
Also and I will bet this is realted. The two concrete constructors do not have a value for _XLocal & _YLocal on the Console.Writeline()s. Without the .ToString() just a "" is printed. Why not?
Thanks.
The issue is that the compiler doesn't know that hg is a derived type of HGOperator_Hello or HGOperator_Goodbye. So inside your if you need to create another variable and cast it:
if (hg is HGOperator_Hello)
{
var helloHg = (HGOperator_Hello)hg;
Console.WriteLine(helloHg._x);
Console.WriteLine(helloHg._XLocal);
}
else
{
var goodbyeHg = (HGOperator_Goodbye)hg;
Console.WriteLine(goodbyeHg._y);
Console.WriteLine(goodbyeHg._YLocal);
}

Categories

Resources