-2

In my Fraction class, I have the numerator = 0 and denominator = 0, both set to private. If I make these public, my calculator GUI shows 0/1 as it's initial variable instead of 0. If they remain private, I get a CS0122, since they cannot access the variables. How can I make them accessible by the GUI and not present as the initial value in the GUI?

GUI CODE

using System;
using System.Windows.Forms;

namespace FractionCalculatorWithGUI_Final
{
    public partial class MainForm : Form
    {
        private enum DISPLAY_MODE { CURRENT_VALUE, ACCUMULATOR }
        private enum FractionValue { numerator, denominator }
        private FractionValue fractionValue = FractionValue.numerator;
        private enum OPERATION { ADD, SUB, MUL, DIV }
        private Fraction accumulator = new Fraction();
        private OPERATION currentOperation = OPERATION.ADD;
        private Fraction currentValue = new Fraction();
        private DISPLAY_MODE displayMode = DISPLAY_MODE.CURRENT_VALUE;

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            UpdateDisplay();
        }

        private void textBoxDisplay_TextChanged(object sender, EventArgs e)
        {

        }

        private void UpdateDisplay()
        {
            switch (displayMode)
            {
                case DISPLAY_MODE.ACCUMULATOR:
                    textBoxDisplay.Text = accumulator.ToString();
                    break;

                case DISPLAY_MODE.CURRENT_VALUE:
                    textBoxDisplay.Text = currentValue.ToString();
                    break;
            }
        }

        private void buttonSlash_Click(object sender, EventArgs e)
        {
            fractionValue = FractionValue.denominator;
            textBoxDisplay.Text = currentValue.ToString() + "/";

            UpdateDisplay();
        }

        private void NumberKeyHit(int number)
        {
            displayMode = DISPLAY_MODE.CURRENT_VALUE;
            if (fractionValue == FractionValue.numerator)
            {
                currentValue.numerator = currentValue.numerator * 10 + number;
            }
            else
            {
                currentValue.denominator = currentValue.denominator * 10 + number;
            }
            UpdateDisplay();
        }

Fraction Class

namespace FractionCalculatorWithGUI_Final
{
    internal class Fraction : IComparable
    {
         private int numerator = 0;
         private int denominator = 1;
        public int CompareTo(object rightObject)
        {
            // typecast the object parameter to a Fraction
            Fraction rightFrac = (Fraction)rightObject;

            // get both Fractions as (comparable) doubles
            double f1 = (double)(this.numerator) / (double)(this.denominator);
            double f2 = (double)(rightFrac.numerator) / (double)(rightFrac.denominator);

            // compare the 2 doubles
            int retVal = 0;
            if (f1 < f2)
                retVal = -1;
            else if (f1 == f2)
                retVal = 0;
            else
                retVal = 1;

            return retVal;
        }
        public override bool Equals(object obj)
        {
            bool equals = false;
            if (obj is Fraction)
                equals = (this == (Fraction)obj);
            return equals;
        }
        public static Fraction Parse(String str)
        {

            Fraction newFraction = new Fraction();

            int indexSlash = str.IndexOf("/");
            if (indexSlash == -1)
            {
                throw new ArgumentException("'/' Character not detected. Please input '/' when writing the fraction.");
            }
            newFraction.numerator = int.Parse(str.Substring(0, indexSlash));
            newFraction.denominator = int.Parse(str.Substring(indexSlash + 1));

            return newFraction;
        }

        public static Fraction operator + (Fraction firstFrac, Fraction secondFrac)
        {
            Fraction sum = new Fraction();
            sum.numerator = ((firstFrac.numerator * secondFrac.denominator) + (secondFrac.numerator * firstFrac.denominator));
            sum.denominator = firstFrac.denominator * secondFrac.denominator;
            return sum;
        }
        public static Fraction operator - (Fraction firstFrac, Fraction secondFrac)
        {
            Fraction difference = new Fraction();
            difference.numerator = ((firstFrac.numerator * secondFrac.denominator) - (secondFrac.numerator * firstFrac.denominator));
            difference.denominator = firstFrac.denominator * secondFrac.denominator;
            return difference;
        }
        public static Fraction operator * (Fraction firstFrac, Fraction secondFrac)
        {
            Fraction product = new Fraction();
            product.numerator = firstFrac.numerator * secondFrac.numerator;
            product.denominator = firstFrac.denominator * secondFrac.denominator;
            return product;
        }
        public static Fraction operator / (Fraction firstFrac, Fraction secondFrac)
        {
            Fraction dividend = new Fraction();
            dividend.numerator = firstFrac.numerator * secondFrac.denominator;
            dividend.denominator = firstFrac.denominator * secondFrac.numerator;
            return dividend;
        }

        

        override public String ToString()
        {
            return numerator.ToString() + "/" + denominator.ToString();
        }
    }
}
stuartd
  • 70,509
  • 14
  • 132
  • 163
  • I expect it shows `0/1` because that’s how you initialize the values - `private int numerator = 0; private int denominator = 1;` – stuartd Mar 03 '23 at 04:06
  • Yes definitely. Is there a way to keep them private but accessible by the GUI code? I also attempted initializing them as {get; set;}, but then arithmetic did not work in the GUI, just showing the answer as 0/0 – FatJohnson6 Mar 03 '23 at 04:14
  • `private` means the fields are only accessible from within the class. If you change them to `public` (or `internal`), then they can be accessed from the GUI class. It seems the issue is more about the default display value of `0/1`, is that right? – Rufus L Mar 03 '23 at 04:16
  • @RufusL yes that is correct – FatJohnson6 Mar 03 '23 at 04:34
  • FYI, If you're going to override `Equals`, you should also override `GetHashCode`. For more info, see [this question](https://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when-equals-method-is-overridden) – Rufus L Mar 03 '23 at 15:56
  • Also, in your `Equals` method that you've overridden, you use the `==` operator to compare the two objects and return the value, but you didn't implement that operator in your code. This means that the default `==` will be used, which is a reference comparison, which will never return true in most cases. – Rufus L Mar 03 '23 at 16:10

1 Answers1

0

In your ToString implementation, you display the value as:

$"{numerator}/{denominator}"

And in the class initialization, you need to make the fields non-private if you want them accessible outside the class (either private or public):

public int numerator = 0;
public int denominator = 1;

You stated:

"If I make these public, my calculator GUI shows 0/1 as it's initial variable instead of 0"

This appears to be the real issue - how the object is displayed. And this is completely up to you in your ToString implementation. You could, for example, always show the fraction as a whole number (i.e. just the numerator) if the denominator is 1:

override public String ToString()
{
    return denominator == 1 
        ? numerator.ToString()
        : $"{numerator}\{denominator}";
}

With this implementation, 0/1 will just display as 0, which is what it sounds like you want.


FYI, I used string interpolation, which is basically shorthand for how you were doing it. If you put a dollar sign in front of the string, then you can put variable names (or just about any object) inside curly braces {} inside the string, and the value there will be replaced with the ToString() implementation of that object.

So $"{numerator}" is essentially the same as numerator.ToString()

And the ?: operator is just shorthand for:

override public String ToString()
{
    if (denominator == 1) 
    {
        return numerator.ToString();
    }
    else 
    {
        return $"{numerator}\{denominator}";
    }
}

UPDATE

Based on your comment and looking at your code, it appears that the issue is with how we're updating the denominator value.

Since it's technically illegal to have a denominator of 0, I would keep the default value set to 1, but in the form code we can store a separate denominator value for our calculations that is initialized to 0:

// This private value is for calculation purposes
private int denominator = 0;

private void NumberKeyHit(int number)
{
    displayMode = DISPLAY_MODE.CURRENT_VALUE;

    if (fractionValue == FractionValue.numerator)
    {
        currentValue.numerator = currentValue.numerator * 10 + number;
    }
    else
    {
        denominator = denominator * 10 + number;
        currentValue.denominator = denominator;
    }

    UpdateDisplay();
}

If you don't like that idea, another way to handle it is in the Fraction class itself, where we can just modify my original solution to include a 0 denominator (I don't like it because I don't think the denominator should ever be 0 since it's illegal in math, but it's up to you):

public int denominator = 0;

override public String ToString()
{
    if (denominator == 0 || denominator == 1) 
    {
        return numerator.ToString();
    }
    else 
    {
        return $"{numerator}\{denominator}";
    }
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • So even when this code works, it still messes with the arithmetic, e.g. the GUI things that the 1 is still in the denominator making the fraction "1/5" actually be entered as "1/15." It unfortunately doesn't solve the issue entirely – FatJohnson6 Mar 03 '23 at 04:51
  • That would be due to some other issue in your code, then. Look at what the code I wrote does: If the denominator is equal to 1, then it just returns the numerator. Otherwise it returns the numerator over the denominator. It doesn't add a `1` in front of anything, so if that's happening, it's elsewhere in your code. – Rufus L Mar 03 '23 at 15:51
  • I see the issue, it's where we're calculating the new denominator in the `NumberKeyHit` method. The code works great as long as the denominator starts at `0`, so I posted two possible solutions. – Rufus L Mar 03 '23 at 18:10