In the following code, I'm trying to learn how to get objects to interact with each other, because I feel that's a bit more important than what I have done now which is simply collect a bunch of variables assign to each object.
For fun, what I want these different objects to do is to kill each other. The person named Jack can kill. The other two can not. What I want Jack to do is strike the other two, making them lose 1, 5 or 10 HitPoints multiple times until they are dead, and then set their Alive to false.
I don't know how to even start this, but I think it would be a very fun and interesting exercise.
The most important thing about doing this is learning how one object can directly change something about a different object, just because it can, and that the objects it has then changed, will then suffer a consequence from this action.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OOP_Learning
{
class Program
{
static void Main(string[] args)
{
Person p1;
p1 = new Person("Jack", 27, true, true, 10);
Person p2;
p2 = new Person("Vincent", 63, true, false, 10);
Person p3;
p3 = new Person("Tim", 13, true, false, 10);
Console.ReadLine();
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool Alive { get; set; }
public bool AbilityToKill { get; set; }
public int HitPoints { get; set; }
public Person(string name, int age, bool alive, bool abilityToKill, int hitPoints)
{
HitPoints = hitPoints;
AbilityToKill = abilityToKill;
Alive = alive;
Name = name;
Age = age;
}
}
}
You need 2 methods in the Person class.
Hit -> This method reduce the HitPoints on self object with each hit. When the HitPoints become Zero, status Alive is set to false.
Kill -> This will take person as a parameter and call Hit method on that person till that person is Alive.
Add following methods to Person class:
public void Hit()
{
if(Alive)
{
if(HitPoints > 0)
{
HitPoints -= 1;
}
else
{
Alive = false;
}
}
}
public bool Kill(Person person)
{
if(!AbilityToKill)
{
Console.WriteLine("You don't have ability to kill! You cannont kill {0}.", person.Name);
return false;
}
while(person.Alive)
{
person.Hit();
}
Console.WriteLine("{0} is dead.", person.Name);
return true;
}
Call Kill method in Main method.
p2.Kill(p3);
p1.Kill(p2);
p1.Kill(p3);
Hope this helps!
Is this the kind of thing you mean?
public class Person
{
public string Name { get; private set; }
public int Age { get; private set; }
public bool Alive { get; private set; }
public bool AbilityToKill { get; private set; }
public int HitPoints { get; private set; }
public void Hit(int points)
{
this.HitPoints -= points;
if (this.HitPoints <= 0)
{
this.HitPoints = 0;
this.Alive = false;
}
}
public Person(string name, int age, bool alive, bool abilityToKill, int hitPoints)
{
this.HitPoints = hitPoints;
this.AbilityToKill = abilityToKill;
this.Alive = alive;
this.Name = name;
this.Age = age;
}
}
Now with the check for AbilityToKill:
public class Person
{
public string Name { get; private set; }
public int Age { get; private set; }
public bool Alive { get; private set; }
public bool AbilityToKill { get; private set; }
public int HitPoints { get; private set; }
public int Attack(Person victim, int points)
{
var hp = victim.HitPoints;
if (this.AbilityToKill)
{
victim.HitPoints -= points;
if (victim.HitPoints <= 0)
{
victim.HitPoints = 0;
victim.Alive = false;
}
}
hp -= victim.HitPoints;
return hp;
}
public Person(string name, int age, bool alive, bool abilityToKill, int hitPoints)
{
this.HitPoints = hitPoints;
this.AbilityToKill = abilityToKill;
this.Alive = alive;
this.Name = name;
this.Age = age;
}
}
This can be used like this:
var jack = new Person("Jack", 27, true, true, 10);
var vincent = new Person("Vincent", 63, true, false, 10);
var tim = new Person("Tim", 13, true, false, 10);
var damage_done = jack.Attack(vincent, 20);
Console.WriteLine(damage_done);
The Attack method returns the actual number of hits points reduced by the attack - the damage dealt.
And here's a more strongly-typed version. Using bool for properties isn't always the clearest way to code. Through in some enums and it's clearer.
public class Person
{
public string Name { get; private set; }
public int Age { get; private set; }
public Alive Alive { get; private set; }
public AbilityToKill AbilityToKill { get; private set; }
public int HitPoints { get; private set; }
public int Attack(Person victim, int points)
{
var hp = victim.HitPoints;
if (this.AbilityToKill == AbilityToKill.Yes)
{
victim.HitPoints -= points;
if (victim.HitPoints <= 0)
{
victim.HitPoints = 0;
victim.Alive = Alive.No;
}
}
hp -= victim.HitPoints;
return hp;
}
public Person(string name, int age, Alive alive, AbilityToKill abilityToKill, int hitPoints)
{
this.HitPoints = hitPoints;
this.AbilityToKill = abilityToKill;
this.Alive = alive;
this.Name = name;
this.Age = age;
}
}
public enum Alive { Yes, No }
public enum AbilityToKill { Yes, No }
It's used like this:
var jack = new Person("Jack", 27, Alive.Yes, AbilityToKill.Yes, 10);
var vincent = new Person("Vincent", 63, Alive.Yes, AbilityToKill.No, 10);
var tim = new Person("Tim", 13, Alive.Yes, AbilityToKill.No, 10);
var damage_done = jack.Attack(vincent, 20);
Console.WriteLine(damage_done);
Let's say I have a class StockMarket which has a list of Companies.
class StockMarket : IStock
{
private static List<IObserverPush> observersPush;
private static List<IObserverPull> observersPull;
public static List<Company> Companies { get; private set; }
public StockMarket()
{
observersPush = new List<IObserverPush>();
observersPull = new List<IObserverPull>();
Companies = new List<Company>() { new Company("Unilever", "UNA", 47.72, 0.77, 1.63, -3.45, "135B"),
new Company("ING Groep", "INGA", 13.40, -0.07, -0.50, -12.38, "60.4B"),
new Company("ArcelorMittal", "MT", 29.50, 0.14, 0.48, 36.05, "54.6B"),
new Company("ASML Holding", "ASML", 167.40, 2.00, 1.21, 36.49, "53.3B"),
new Company("Heineken", "HEIA", 87.66, -0.02, -0.02, 2.80, "49B"),
new Company("RELX", "REN", 18.15, 0.17, 0.95, -0.22, "38.9B"),
new Company("Philips", "PHIA", 35.49, 0.17, 0.47, 7.61, "33.3B"),
new Company("Unibail Rodamco", "UL", 196.40, -0.15, -0.08, -16.78, "20.3B"),
new Company("Akzo Nobel", "AKZA", 75.68, -0.16, -0.21, 0.33, "19.4B"),
new Company("Altice", "ATC", 7.58, 0.16, 2.16, -66.30, "17.6B")};
Thread thread = new Thread(SimulateMarket);
thread.Start();
}
public void Subscribe(IObserverPull o)
{
observersPull.Add(o);
o.UpdateMarket();
}
public void Unsubscribe(IObserverPull o)
{
observersPull.Remove(o);
}
public void Subscribe(IObserverPush o)
{
observersPush.Add(o);
o.UpdateMarket(Companies);
}
public void Unsubscribe(IObserverPush o)
{
observersPush.Remove(o);
}
public void NotifyObservers()
{
foreach(IObserverPush o in observersPush)
{
o.UpdateMarket(Companies);
}
foreach(IObserverPull o in observersPull)
{
o.UpdateMarket();
}
}
public void SimulateMarket()
{
while(observersPush.Count + observersPull.Count > 0)
{
//randomly change property values of companies
//and notify the observers about the changes
}
}
}
Company class has some properties.
public class Company
{
public string Name { get; private set; }
public string Symbol { get; private set; }
public double Price { get; set; }
public double Change { get; set; }
public double ChangePercentageDay { get; set; }
public double ChangePercentageYear { get; set; }
public string Capital { get; private set; }
public Company(string name, string symbol, double price, double change, double changePercentageDay,
double changePercentageYear, string capital)
{
Name = name;
Symbol = symbol;
Price = price;
Change = change;
ChangePercentageDay = changePercentageDay;
ChangePercentageYear = changePercentageYear;
Capital = capital;
}
}
The Forms have references to the StockMarket and they use it to retrieve data about the companies and to display it.
Form 1
public partial class ConcreteObserverPush : Form, IObserverPush
{
private StockMarket stockMarket;
public ConcreteObserverPush()
{
InitializeComponent();
stockMarket = new StockMarket();
stockMarket.Subscribe(this);
}
public void UpdateMarket(List<Company> companies)
{
stockMarketListView.Items.Clear();
foreach(Company c in companies)
{
ListViewItem item = new ListViewItem(c.Symbol);
item.SubItems.Add(c.Price.ToString());
item.SubItems.Add(c.Change.ToString());
item.SubItems.Add(c.ChangePercentageDay.ToString() + "%");
stockMarketListView.Items.Add(item);
}
}
private void ConcreteObserverPush_FormClosing(object sender, FormClosingEventArgs e)
{
stockMarket.Unsubscribe(this);
}
}
Form 2
public partial class ConcreteObserverPull : Form, IObserverPull
{
private StockMarket stockMarket;
public ConcreteObserverPull()
{
InitializeComponent();
stockMarket = new StockMarket();
stockMarket.Subscribe(this);
}
public void UpdateMarket()
{
stockMarketListView.Items.Clear();
foreach (Company c in StockMarket.Companies)
{
ListViewItem item = new ListViewItem(c.Symbol);
item.SubItems.Add(c.Name);
item.SubItems.Add(c.Price.ToString());
item.SubItems.Add(c.Change.ToString());
item.SubItems.Add(c.ChangePercentageDay.ToString() + "%");
item.SubItems.Add(c.ChangePercentageYear.ToString() + "%");
item.SubItems.Add(c.Capital);
stockMarketListView.Items.Add(item);
}
}
private void ConcreteObserverPull_FormClosing(object sender, FormClosingEventArgs e)
{
stockMarket.Unsubscribe(this);
}
}
The problem is that if the Form gets the list of companies through the property on StockMarket it can change their state. However, I want only StockMarket to have the ability to change the state of the company.
So what would be the best way to share Company state with Form when requested and preventing the Form from modifying it.
I know that a possible solution would be to return clones of Company objects, but I believe there should be a better solution.
Any help is appreciated!
The general gist of this would be to make your Company object immutable. Then you would add methods to the StockMarket object to manipulate the Company list and replace entries with new ones when you want to change a value.
Here's a quick example put together in LINQPad of making the Company class immutable and adding an UpdatePrice method to the StockMarket class.
Whether you want to be able to manipulate the Companies property from outside the StockMarket can be handled by returning the list as ReadOnlyCollection so that it's size can't be manipulated by a consumer.
void Main()
{
var sm = new StockMarket();
sm.Companies.Add(new Company("Test", "TST", 50, 0));
sm.UpdatePrice("Test", 45);
var testCompany = sm.Companies.First(x => x.Name == "Test");
Console.WriteLine($"{testCompany.Name},{testCompany.Symbol},{testCompany.Price},{testCompany.Change}");
//Output: Test,TST,45,-5
}
class StockMarket
{
public List<Company> Companies { get; private set; } = new List<Company>();
public void UpdatePrice(string name, double price) {
var index = Companies.FindIndex(x => x.Name == name);
if(index >= 0)
{
var previous = Companies[index];
Companies[index] = new Company(previous.Name, previous.Symbol, price, price - previous.Price);
}
}
}
class Company
{
public Company(string name, string symbol, double price, double change) {
Name = name;
Symbol = symbol;
Price = price;
Change = change;
}
public string Name { get; }
public string Symbol { get; }
public double Price { get; }
public double Change { get; }
///...
}
This would be a solution:
Create the Company class as a Private Inner Class inside of the StockMarket class, that way it'd only be accessible inside of it, and then provide an interface that only includes the get of all the properties and make Company implement it. You would have to make StockMarket's Company list to be the Interface's type.
Any modification you'd have to do you'd do it by casting the interface's List objects into the original class type.
Example:
class Program
{
public static StockMarket stockMarket = new StockMarket();
static void Main(string[] args)
{
}
}
public interface ICompany
{
string Name { get; }
}
public class StockMarket
{
public StockMarket()
{
Companies = SomeWildFunctionThatRetrievesAllCompanies();
}
public void OneWildFunctionThatModifiesACompany()
{
Company dunno = (Company)Companies[0];
dunno.Name = "Modification Made Possible";
}
private List<ICompany> SomeWildFunctionThatRetrievesAllCompanies()
{
return new List<ICompany>(new List<Company>());
}
public List<ICompany> Companies { get; private set; }
private class Company : ICompany
{
public string Name { get; set; }
}
}
Try this:
class Company
{
public Company(Type type,string name,string symbol,double price, double change)
{
if (type.Name == "StockMarket")
{
Name = name;
Symbol = symbol;
Price = price;
Change = change;
}
}
private string Name { get; set; }
private string Symbol { get; set; }
private double Price { get; set; }
private double Change { get; set; }
///...
}
This will allow you to change the state only if the type is StockMarket
like:
class StockMarket
{
public List<Company> Companies { get; set; }
public StockMarket()
{
Companies = new List<Company>();
}
public StockMarket someMethod()
{
//You can change the state here
StockMarket s = new StockMarket();
s.Companies.Add(new Company(this.GetType(), "aa", "_", 123, 1234));
return s;
}
//...
}
Now you cannot change the state here:
public partial class Observer: Form
{
private StockMarket stockMarket;
public ConcreteObserverPull()
{
InitializeComponent();
stockMarket = new StockMarket();
//Here you cannot change the state
stockMarket.Companies.Add(new Company(this.GetType(), "aa", "_", 123,12));
}
//...
}
Sorry, I don't know C#, but as an idea, you can wrap returned entities with decorator or proxy, which will throw an exception in case of trying to modify state of a company.
Returning clones with fields set as readonly is the safest way to go.
I want to call a method for my WPF-App with subtype objects of my Piece class. My problem is that the subtype objects have more properties than e.g the the Text objects.
Do you know a way to cope with this better than I do in my FillForm example?
namespace Namespace
{
public abstract class Piece
{
public int id { get; set; }
public string title { get; set; }
public string description { get; set; }
}
public class Text : Piece
{
}
public class Image: Piece{
public string filePath { get; set; }
public string fileformat { get; set; }
}
public class Video : Image
{
}
}
}
Example method:
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
if (!currentPiece.GetType().ToString().Equals("Namespace.Text"))
{
pieceFileSelectURLTextBlock.Text = (currentPiece as Namespace.Image).filePath;
SetPreviews((currentPiece as Namespace.Image).filePath);
}
}
Thanks!
Why not just change the method to the following with more type-safety
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
if (currentPiece as Namespace.Image imagePiece)
{
pieceFileSelectURLTextBlock.Text = imagePiece.filePath;
SetPreviews(imagePiece.filePath);
}
}
Do a safecast:
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
var imagePiece = currentPiece as Image;
if(imagePiece != null)
pieceFileSelectURLTextBlock.Text = imagePiece .filePath;
SetPreviews(imagePiece .filePath);
}
}
I am developing an app for windows phone 8.1, I have three pages, I'm using the Frame.Navigate(typeof(Page2),nav) to pass data between pages, I'm using a class
public class PassedData
{
public string Name { get; set; }
public string Name1 { get; set; }
public string Name3 { get; set; }
public int Value { get; set; }
}
for the object:
var nav = (passedData)e.Parameter;
myTextBlock.Text = nav.Name;
mytex1.Text = nav.Name1;
my problem how do the object for the int value? I have a problem with the conversion type int in the object.
just try this
int convertValue = 0;
var passedData= new PassedData;
passedData.Name="Name"; passedData.Name1="Name1"; passedData.Name3="Name3";
long number1 = 0;
bool canConvert = long.TryParse(myTextBox.Text, out convertValue );
if (canConvert == true)
{
passedData.Value= Convert.ToInt32(myTextBox.Text);
}
I think it will works
I'm using C# and have created an object to send to a JSON service that looks like this:
public class SendRequest
{
public string id { get; set; }
public string case { get; set; }
public string method { get; set; }
public Volume value { get; set; }
}
public class Volume
{
public int level;
public bool mute;
}
I can code hint when setting the sub object:
var _req = new SendRequest();
_req.value.mute = false;
_req.value.level = 50;
But when the program is run, the sub-object itself is null (_req.value = null) and the two items under that object don't show.
Why is this happening?
You need to initialize the "value" to something.
Add this constructor to your SendRequest class:
public SendRequest(){ value = new Volume(); }
You can use object initializers
var _req = new SendRequest()
{
value = new Volume()
{
mute = false,
level = 50,
},
};
at this point method is null
public string method { get; set; }
longer but what you can do is
private string method = string.empty;
public string Method { get {return method;} set {method = value;} }
value is just a bad name
private Volume volume = new Volume();
public Volume Volume { get {return volume;} set {volume = value;} }
or
volume = new Volume (mute = false, value.level = 50);