Event can't be raised - c#

I am trying to do an assignment given but I am failing to do so. I wrote a product class and made a flower from it. Then I want to raise an event when flower quantity falls below 20, It should give me a warning. I think I am having difficulty in raising the event. I am sure I made the declerations of delegate and event correct but something is missing. Thank you in advance.
This line
flower.StockDecreased();
gives me this error:
Error 3 The event 'StokTakip.Product.StockDecreased' can only appear on the left hand side of += or -= (except when used from within the type 'StokTakip.Product')
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StokTakip
{
class Program
{
static void FlowerStockDecreased()
{
Console.WriteLine("Flower stock decreased");
}
static void Main(string[] args)
{
Product flower = new Product("Flower", 50);
Console.WriteLine(flower);
flower.StockDecreased += new Product.FlowerEventHandler(FlowerStockDecreased);
while (true)
{
Console.WriteLine("What is your choice");
Console.WriteLine("[1] Stock entry quantity ");
Console.WriteLine("[2] Stock exit quantity: ");
int choice = Convert.ToInt32(Console.ReadLine());
if (choice == 1)
{
Console.Write("Enter stock entry quantity: ");
flower.quantity += Convert.ToInt32(Console.ReadLine());
}
else if (choice == 2)
{
Console.Write("Enter stock exit quantity: ");
flower.quantity -= Convert.ToInt32(Console.ReadLine());
}
Console.WriteLine(flower);
if (flower.quantity<20)
{
flower.StockDecreased(); //????
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StokTakip
{
public class Product
{
public string name;
public int quantity;
public Product(string a, int m)
{
name = a;
quantity = m;
}
public override string ToString()
{
return "Name: "+ this.name + " Stock Quantity: " + this.quantity;
}
public delegate void FlowerEventHandler();
public event FlowerEventHandler StockDecreased;
}
}

The error message is pretty clear. You can not raise an event like that. Only the declaring class can call the event like this and thus raise the event. All other classes can only add (+=) or remove (-=) event handlers from the event.
What you can do is put a public method into the Product class that raises the event like this:
public void RaiseStockDecreased()
{
if (StockDecreased != null)
StockDecreased();
}
You could call that externally.
But then again, this contradicts proper design, as I'd expect the Product class itself to determine whether stock was increased or decreased and raise the proper events. Otherwise you have to implement that logic in every place you want to be notified about stock changes.

Your product class should look something like this:
public class Product
{
public event FlowerEventHandler StockDecreased;
private int _quantity;
public int Quantity
{
get { return _quantity; }
set
{
int newQuantity = value;
if (newQuantity < _quantity)
{
if (StockDecreased != null) StockDecreased();
}
_quantity = newQuantity;
}
}
// Other stuff
}
Though a better pattern might be to have a StockChange method that can just check whether the change is positive or negative and raise the appropriate event.

Related

c# subscribe an event when a function is complete?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp13
{
class Program
{
public class Subscriber
{
public static void Main()
{
Publisher publisher = new Publisher();
publisher.BeginAdd += AddCallback;
publisher.EndAdd += EndCallBack;
Console.WriteLine(publisher.Multiply(2.3f, 4.5f));
publisher.BeginAdd -= AddCallback;
publisher.EndAdd -= EndCallBack;
Console.WriteLine(publisher.Multiply(3.3f, 4.4f));
Console.ReadLine();
}
public static void AddCallback(string message)
{
Console.WriteLine("Callback - " + message);
}
public static void EndCallBack(string message)
{
Console.WriteLine("Callback - " + message);
}
}
public class Publisher
{
public delegate void Notify(string message); // Declare delegate.
public event Notify BeginAdd; // Declare event.
public event Notify EndAdd;
public float Multiply(float a, float b)
{
OnBeginAdd(); // Raise event.
OnEndAdd();
return a * b;
}
private void OnBeginAdd()
{
if (BeginAdd != null)
BeginAdd("Starting multiplication!"); // Call callback method.
}
private void OnEndAdd()
{
if (EndAdd != null)
EndAdd("Completing multiplication!");
}
}
}
}
How do I correct the syntax for adding OnEndAdd(); into the Multiply function so that it only does the call back after the completion of the function? I've tried adding it after the return statement but that obviously doesn't work, can't seem to figure out another way...
Once the Multiply function has returned the control moves away from the publisher, so some design changes would be necessary here.
Do you perhaps mean on completion of the multiply operation (and not the necessarily the entire function call), the below change would suffice.
public float Multiply(float a, float b)
{
OnBeginAdd();
var result = a * b;
OnEndAdd();
}
A prettier (tm) approach could be to create another class called e.g. OperationScope of type IDisposable which calls the OnBeginAdd / OnEndAdd for you - for example:
public float Multiply(float a, float b)
{
using (new OperationScope(this)) //This is IDisposable and calls OnBeginAdd & OnEndAdd
{
return a * b;
}
}
NOTE: There are potentially other similar ways instead of using an IDisposable class, such as passing a Func<xyz> that does the actual work (multiply) to another method which calls OnBeginAdd/OnEndAdd.

From a Class to a Form

EDIT: I have tried this a different way and updated my new code. I am now getting a few errors:
'Car_Class_BBrantley.Car.Car()' must declare a body because it is not marked abstract, extern, or partial
No overload for method 'GetCarData' takes 0 arguments
No overload for method 'GetCarData' takes 0 arguments
These last two errors fall under the GetCarData(); lines which are under the two button sections.
Alright, so my task is to create an application that displays 3 main features: year, make, and speed of a car. The year and make are inputted with textboxes and the speed starts at 0.
There is an accelerate button which is supposed to add 5 to the speed every time it is pressed and a brake button which decreases the speed by 5 every time it is pressed.
I am having trouble using the class and form together to display the results. I need to display in a messagebox the make, year, and speed. I have been sitting here for hours and I am getting nowhere. I am getting the errors " speed does not exist in current context" and "car does not exist in current context" under my buttons. I am unsure of how I should go about fixing this.
Any and all help is much appreciated. I'm sorry if this is a mess. I have never worked with classes before.
Here is the form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Car_Class_BBrantley
{
public partial class Form1 : Form
{
private Car myCar;
public Form1()
{
myCar = new Car;
InitializeComponent();
}
private void GetCarData(Car car)
{
try
{
myCar.Make = txtMake.Text;
myCar.Year = int.Parse(txtModel.Text);
myCar.Speed = 0;
}
catch (Exception ex)
{
MessageBox.Show(string.Concat("Must enter a valid make and year model for the car. ", ex.Message, "\r\n", ex.StackTrace));
}
}
private void btnAcc_Click(object sender, EventArgs e)
{
GetCarData();
myCar.AccSpeed(5);
MessageBox.Show(" Your car is a " + myCar.Year + myCar.Make + " and it is traveling " + myCar.Speed + " mph. ");
}
private void btnBrake_Click(object sender, EventArgs e)
{
GetCarData();
myCar.DecSpeed(5);
MessageBox.Show(" Your car is a " + myCar.Year + myCar.Make + " and it is traveling " + myCar.Speed + " mph. ");
}
}
}
If you would like to see the class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Car_Class_BBrantley
{
class Car
{
private int year;
private string make;
private int speed;
public Car()
{
this.year = 1994;
this.make = "Ford";
this.speed = 0;
}
public Car(string make, int year, int speed)
{
this.year = year;
this.make = make;
this.speed = speed;
}
public string Make
{
get { return make; }
set { make = value; }
}
public int Year
{
get { return Year; }
set { Year = value; }
}
public int Speed
{
get { return speed; }
set { speed = value; }
}
public void AccSpeed(int speedIncrement)
{
//Add check for speed limit ranges
Speed += speedIncrement;
}
public void DecSpeed(int speedDecrement)
{
//Add check for speed limit ranges
Speed -= speedDecrement;
}
}
}
You haven't created an instance of the Car class.
In this method:
private void btnAcc_Click(object sender, EventArgs e)
{
GetCarData(car);
car.Accelerate += speed;
MessageBox.Show(" Your car is a " + car.year + car.make + " and it is traveling " + speed + " mph. ");
}
You're trying to do something with car (GetCarData(car))but... what car?
Create an instance of the Car class at the form level:
public partial class Form1 : Form
Car car = new Car();
.
.
You have to store the car somewhere within the form. If you need to use more cars, you should have some kind of collection, selecting a correct car depending on some of the parameters.
A simple example would be:
public partial class Form1 : Form
{
private Car _car;
public Form1()
{
_car = new Car();
InitializeComponent();
}
...
and then use the already created instance of car within the form, i.e.
private void btnAcc_Click(object sender, EventArgs e)
{
GetCarData(_car);
_car.Accelerate(); //note the way you were using won't work on this line
MessageBox.Show(" Your car is a " + _car.year + _car.make + " and it is traveling " + speed + " mph. ");
}
In my opinion, you may need to add another button to set the initial settings of the car, in that event handler method you initialize the car's Year, Speed and Model info by calling the GetCarData(). Then, remove the GetCarData() from the DecSpeed() and AccSpeed(), because you don't want to re-initialize everytime you Accelerate or Decelerate.

Incrementing the Speed with Every Button Click

I have a working application that compiles and everything. The two buttons work, however I need them to add/decrease 5 from the speed with every single click and currently they will only let me click once and accelerate gives me an answer of 5 for speed, while decelerate gives -5. How can I change this to allow for each button to be clicked and the speed update more than once?
Here is the form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Car_Class_BBrantley
{
public partial class Form1 : Form
{
private Car myCar;
public Form1()
{
myCar = new Car();
InitializeComponent();
}
private void GetCarData()
{
try {
myCar.Make = txtMake.Text;
myCar.Year = int.Parse(txtModel.Text);
myCar.Speed = 0;
}
catch (Exception ex)
{
MessageBox.Show(string.Concat("Must enter a valid make and year model for the car. ", ex.Message, "\r\n", ex.StackTrace));
}
}
private void btnAcc_Click(object sender, EventArgs e)
{
GetCarData();
myCar.AccSpeed(5);
lblAnswer.Text = " Your car is a " + myCar.Year + " " + myCar.Make + " and it is traveling " + myCar.Speed + " mph. ";
}
private void btnBrake_Click(object sender, EventArgs e)
{
GetCarData();
myCar.DecSpeed(5);
lblAnswer.Text = " Your car is a " + myCar.Year + " " + myCar.Make + " and it is traveling " + myCar.Speed + " mph. ";
}
}
}
Here is the class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Car_Class_BBrantley
{
class Car
{
private int year;
private string make;
private int speed;
public Car()
{
this.year = 1994;
this.make = "Ford";
this.speed = 0;
}
public Car(string make, int year, int speed)
{
this.year = year;
this.make = make;
this.speed = speed;
}
public string Make
{
get { return make; }
set { make = value; }
}
public int Year
{
get { return year; }
set { year = value; }
}
public int Speed
{
get { return speed; }
set { speed = value; }
}
public void AccSpeed(int speedIncrement)
{
//Add check for speed limit ranges
Speed += speedIncrement;
}
public void DecSpeed(int speedDecrement)
{
//Add check for speed limit ranges
Speed -= speedDecrement;
}
}
}
Since this is probably homework I'll just give hints. With both click functions, the first call you make is to GetCarData(). Inside GetCarData(), you're calling myCar.Speed = 0;, so it's not surprising that you're going back to square one with each click.
Assuming that you want only one instance of Car for your entire application, you just need to modify the insides of GetCarData() to do (or not do) what you want. Just a few tweaks in that function, which I'll let you experiment with, and you'll be good to go.
However, if you want multiple Car objects in your app, you're going to need to modify it to have some kind of List (or better yet, an IDictionary).

Try catch statements with window forms application

My goal is to throw a NegativeBalanceException if there is an attempt to take the balance below zero. Just like if this was a real bank account. I had if and else statements, but butchered them up and am trying to learn try, catch, and throw statements (was reading on finally as well but I don't think that applies here). Anyway, I set up where a catch statement works if I just hit the deposit button without typing anything. But, I don't understand where it is wanting me to implement it in for taking it below zero. Is it in my deposit method? Or is it in the actual btn_deposit? Also, what is the purpose of using try catch statements over if else statements? I am new to programming and am just trying to learn.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public class BankAccount
{
decimal amount = 300.00m;
// Declare Delegate Type Object
public delegate void BankDelegate(decimal oldBalance, decimal newBalance);
// Create Delegate Type Events
public event BankDelegate OnDeposit;
public event BankDelegate OnWithdraw;
public void Deposit(decimal a)
{
{
if (a > 0)
{
OnDeposit(this.amount, this.amount + a);
this.amount += a;
}
else
{
MessageBox.Show("No!");
}
}
}
public void Withdraw(decimal a)
{
// Fire OnWithdraw Event and pass old and new balance amount
OnWithdraw(this.amount, this.amount - a);
this.amount -= a;
}
}
// Declare BankAccount class variable
BankAccount account = null;
private void Form1_Load(object sender, EventArgs e)
{
account = new BankAccount();
// Attach Event Handlers with Events
account.OnDeposit += new BankAccount.BankDelegate(account_OnDeposit);
account.OnWithdraw += new BankAccount.BankDelegate(account_OnWithdraw);
}
private void btnDeposit_Click(object sender, EventArgs e)
{
try
{
account.Deposit(Convert.ToDecimal(textBox1.Text));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnWIthdraw_Click(object sender, EventArgs e)
{
account.Withdraw(Convert.ToDecimal(textBox1.Text));
}
void account_OnDeposit(decimal oldBalance, decimal newBalance)
{
label4.Text = oldBalance.ToString();
label5.Text = newBalance.ToString();
}
void account_OnWithdraw(decimal oldBalance, decimal newBalance)
{
label4.Text = oldBalance.ToString();
label5.Text = newBalance.ToString();
}
}
}
You should only throw exceptions for exceptional circumstances. Overdrawn accounts are not an exceptional circumstance.. but depositing a negative amount is.
Therefore, I would do something like this for the Deposit method:
public void Deposit(decimal a)
{
if (a < 1)
throw new NegativeDepositException("You cannot deposit this amount");
OnDeposit(this.amount, this.amount + a);
this.amount += a;
}
BUT.
You should be validating this prior to entering the method. That way, the exception should never be called - unless you call Deposit from another method without the check - which would be exceptional.
private void btnDeposit_Click(object sender, EventArgs e)
{
// try..catch removed. This will now crash if you forget to check the value.
var amount = Convert.ToDecimal(textBox1.Text);
if (amount < 1)
MessageBox.Show("You cannot deposit this amount");
else
account.Deposit(amount);
}
Also, I would change Withdraw to return bool, since overdrawing is not really an exceptional circumstance:
public bool Withdraw(decimal a)
{
if (this.amount - a >= 0)
{
// Fire OnWithdraw Event and pass old and new balance amount
OnWithdraw(this.amount, this.amount - a);
this.amount -= a;
return true; // successful
}
else
{
return false; // unsuccessful
}
}
Then when you call it:
private void btnWIthdraw_Click(object sender, EventArgs e)
{
if (!account.Withdraw(Convert.ToDecimal(textBox1.Text)))
{
MessageBox.Show("Insufficient funds");
}
}
EDIT:
In response to your comment. You must create your own exception class if you want it to be named a specific way (or if you want it to extend the functionality of a normal Exception). In my example, you would have to create this:
public class NegativeDepositException : Exception {
}
Thats it. It gets everything it needs from Exception.. for now.

Console.WriteLine not working?

I've created a program which should calculate the surface area of an irregular shaped object, such as a lake.
I read in a file, which contained the values for the x and y values, and the depth.
I'm new to C#, and so I don't fully understand everything yet, but I think my code should work, however, it doesn't seem to be writing the value for the area onto the screen.
I know that Console.WriteLine(_surface); SHOULD work , but it I can't seem to get it to do anything, and it's probably in the wrong place!
Can someone please tell me where I'm going wrong?
My code is as follows.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NUnit.Framework;
namespace ConsoleApplication1
{
public class ValueXyz
{
public double X { get; set; }
public double Y { get; set; }
public int Z { get; set; }
}
public class SurfaceCalculator
{
private ValueXyz[] _valuesXyz;
private double _surface;
private readonly string _textWithValues;
public SurfaceCalculator(string textWithValues)
{
_textWithValues = textWithValues;
SetValuesToCalculate();
}
public double Surface
{
get { return _surface; }
}
public void CalculateSurface()
{
for (var i = 0; i < _valuesXyz.Length; i++)
{
if (_valuesXyz[i].Z == 0)
_surface = (_valuesXyz[i].X * _valuesXyz[i + 1].Y) - (_valuesXyz[i + 1].X * _valuesXyz[i].Y);
Console.WriteLine(_surface);
}
}
private void SetValuesToCalculate()
{
var valuesXyz = _textWithValues.Split(' ');
_valuesXyz = valuesXyz.Select(item => new ValueXyz
{
X = Convert.ToDouble(item.Split(',')[0]),
Y = Convert.ToDouble(item.Split(',')[1]),
Z = Convert.ToInt32(item.Split(',')[2])
}).ToArray();
}
public void TestSurfaceCalculatorGetsAValue()
{
var textWithValues = File.ReadAllLines(#"C:\Users\user\Documents\Visual Studio 2010\Projects\Lake_Take_Toooooo\Lake_Take_Toooooo\bin\Debug\Lake_Test.csv");
var calculator = new SurfaceCalculator(_textWithValues);
calculator.CalculateSurface();
Assert.IsNotNull(calculator.Surface);
}
static void Main()
{
Console.ReadKey();
}
}
}
This is my first time using classes, so apologies if there's an obvious answer.
Thanks for your help!
You need to actually call the method inside the Main method, which is the program entry point. Like:
static void Main()
{
CalculateSurface();
Console.ReadKey();
}
When you run your program, only the code inside the Main method is actually executed. If you do not call anything from there then no code is executed.
No function is being called in the Main event...
I should imagine read key will wait for key input then close, correct?
Are you trying to run this as console application or as unit test? (It looks like you're trying to run it as unit test since you're using NUnit.Framework and there is a Test-method with an Assert...)
If you want to run it as console application, you have to call the code that should get executed in the Main method.
If you want to run it as unit test, you have to add some "attributes" to your test class and test method. The class should have the [TestFixture] attribute, and the method should have the [Test] attribute, like:
[TestFixture]
public class SurfaceCalculator {
...
[Test]
public void TestSurfaceCalculatorGetsAValue() {
...
}
}

Categories