Understanding classes and using Random [closed] - c#

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
I've written the following class to return a random number like rolling a dice:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace GameTest
{
class Dice
{
public int publicMinNum
{
get { return _minNum; }
set { _minNum = value; }
}
public int publicMaxNum
{
get { return _maxNum; }
set { _maxNum = value; }
}
static int _minNum;
static int _maxNum;
static Random diceRoll = new Random();
public int rolled = diceRoll.Next(_minNum, _maxNum);
}
}
This class is called a couple of times in my form:
private void btnPushMe_Click(object sender, EventArgs e)
{
Dice myRoll = new Dice();
myRoll.publicMinNum = 1;
myRoll.publicMaxNum = 7;
lblMain.Text = myRoll.rolled.ToString();
Dice mySecondRoll = new Dice();
mySecondRoll.publicMinNum = 1;
mySecondRoll.publicMaxNum = 13;
lblMain2.Text = mySecondRoll.rolled.ToString();
}
As you can see, I call the class twice as myRoll and mySecondRoll. I thought by doing this it would create separate instances of the class and output two separate numbers (one between 1 and 6, the other 1 and 12)
The problems I am having are:
1) the first number out is always 0.
2) the two instances of the class interfere with one another, ie. the number that should be between 1 and 6 just isn't.
I'm wondering, not just how to fix the code, but would also like an explanation of what is happening here and why, thanks.

The problem is that you are declaring the fields in the Dice class as static. This means that there will only be one instance of that variable, which will be shared across all instances of the class within the application.
The following line:
public int rolled = diceRoll.Next(_minNum, _maxNum);
... gets run the moment you create your new Dice(), which means that you haven't yet initialized your _minNum and _maxNum values yet: that's why it's giving you a 0. You could turn this into a property, so the code would wait to be run until you asked for it:
public int Rolled { get { return diceRoll.Next(_minNum, _maxNum); } }
... but typically properties are not expected to change just by asking for their value. This sort of code tends to create so-called Heisenbugs, which are very difficult to track down because the system's behavior changes simply by trying to observe it.
So here's one way you might re-write your class, using a Roll() method to actually perform the roll, and a property that allows code to keep checking on the last roll's value whenever necessary:
public class Die
{
// Using a constructor makes it obvious that you expect this
// class to be initialized with both minimum and maximum values.
public Die(int minNum, int maxNum)
{
// You may want to add error-checking here, to throw an exception
// in the event that minNum and maxNum values are incorrect.
// Initialize the values.
MinNum = minNum;
MaxNum = maxNum;
// Dice never start out with "no" value, right?
Roll();
}
// These will presumably only be set by the constructor, but people can
// check to see what the min and max are at any time.
public int MinNum { get; private set; }
public int MaxNum { get; private set; }
// Keeps track of the most recent roll value.
private int _lastRoll;
// Creates a new _lastRoll value, and returns it.
public int Roll() {
_lastRoll = diceRoll.Next(MinNum, MaxNum);
return _lastRoll;
}
// Returns the result of the last roll, without rolling again.
public int LastRoll {get {return _lastRoll;}}
// This Random object will be reused by all instances, which helps
// make results of multiple dice somewhat less random.
private static readonly Random diceRoll = new Random();
}
(note that "die" is the singular form of "dice"). Usage:
private void btnPushMe_Click(object sender, EventArgs e)
{
Die myRoll = new Die(1, 7);
lblMain.Text = myRoll.Roll().ToString();
Die myRoll2 = new Die(1, 13);
lblMain2.Text = mySecondRoll.Roll().ToString();
}

Question Two is already aswered: because the variables are static:
static int _minNum;
static int _maxNum;
Question One on the other hand isnt answered yet, so here goes:
public int rolled = diceRoll.Next(_minNum, _maxNum);
this is not some dynamic call. This is a field initialisation, and will be set even before the constructor. You can check this out by debugging through the dice the first time.
at that point both _minNum and _maxNum are still 0, so rolled will be set to 0
this can be fixed by turning rolled into a property too:
public int rolled
{
get { return diceRoll.Next(_minNum, _maxNum); }
}
At the moment _minNum and _maxNum are getting set the first time because they are static, therefor when you create the second dice, they are already set.
Edit, since a recommendation was asked, this is how I'd create it:
The dice
class Dice
{
private static Random diceRoll = new Random();
private int _min;
private int _max;
public int Rolled { get; private set; }
public Dice(int min, int max)
{
_min = min;
_max = max;
// initializes the dice
Rolled = diceRoll.Next(_min, _max);
}
public int ReRoll
{
get
{
Rolled = diceRoll.Next(_min, _max);
return Rolled;
}
}
}
Note the the dice has two properties: Rolled, and ReRoll. Because your intention is unclear, I've added both to illustrate the behavior.
Rolled is set by the constructor. If you want a new number, you can ReRoll.
If you do intentionally wanted the lifetime of a roll to be one per dice (but I dont think so) You'd remove the ReRoll method.
The dice would be called like this:
private static void Main(string[] args)
{
Dice myRoll = new Dice(1, 7);
// All the same
var result1 = myRoll.Rolled.ToString();
var result2 = myRoll.Rolled.ToString();
var result3 = myRoll.Rolled.ToString();
// something new
var result4 = myRoll.ReRoll.ToString();
Dice mySecondRoll = new Dice(1, 13);
var result = mySecondRoll.ReRoll.ToString();
}

Your get/setter backing fields are marked as "static". If a variable is declared "static", the value is persisted throughout the application and shared between different instances of the type they live in.
See here.
Also,
since your class properties contain no logic, I suggest using "automatic" properties.
class Dice
{
public int publicMinNum { get; set; }
public int publicMaxNum { get; set; }
Random diceRoll = new Random();
public int rolled = diceRoll.Next(publicMinNum , publicMaxNum );
}
tutorial on automatic properties here.

Your problem is due to the static members.
From the MSDN documentation on static, "While an instance of a class contains a separate copy of all instance fields of the class, there is only one copy of each static field."

I would change your class to look more like the following:
class Dice
{
// These are non-static fields. They are unique to each implementation of the
// class. (i.e. Each time you create a 'Dice', these will be "created" as well.
private int _minNum, _maxNum;
// Readonly means that we can't set _diceRand anywhere but the constructor.
// This way, we don't accidently mess with it later in the code.
// Per comment's suggestion, leave this as static... that way only one
// implementation is used and you get more random results. This means that
// each implementation of the Dice will use the same _diceRand
private static readonly Random _diceRand = new Random();
// A constructor allows you to set the intial values.
// You would do this to FORCE the code to set it, instead
// of relying on the programmer to remember to set the values
// later.
public Dice(int min, int max)
{
_minNum = min;
_maxNum = max;
}
// Properties
public Int32 MinNum
{
get { return _minNum; }
set { _minNum = value; }
}
public Int32 MaxNum
{
get { return _maxNum; }
set { _maxNum = value; }
}
// Methods
// I would make your 'rolled' look more like a method instead of a public
// a variable. If you have it as a variable, then each time you call it, you
// do NOT get the next random value. It only initializes to that... so it would
// never change. Using a method will have it assign a new value each time.
public int NextRoll()
{
return _diceRand.Next(_minNum, _maxNum);
}
}

I think the real problem here is that you haven't quite modeled a Die properly.
A die has a min and max value (that define the start and end of a range) but once a die has been made you are not able to change this i.e. a six sided die isn't made into an eight sided die. As such there is no need for the public setters.
Now, not all die share the same range, this is something that is specific to each die and so these properties should belong to the instance and not be static.
Again each die object has a CurrentRoll value representing the number that is face up and this is indeed generated at random. However to change the CurrentRoll of a die you need to Roll it.
This leaves an implementation of Die looking a something like
class Die
{
private static Random _random;
public int CurrentRoll { get; private set; }
public int Min { get; private set; }
public int Max { get; private set; }
public Die(int min, int max)
{
Min = min;
Max = max;
Roll();
}
public int Roll()
{
CurrentRoll = _random.Next(Min, Max+1); // note the upperbound is exlusive hence +1
return CurrentRoll;
}
}
and you would use it like
public static void Main()
{
Die d1 = new Die(1, 6);
Die d2 = new Die(1, 6);
Console.WriteLine(d1.Roll());
Console.WriteLine(d2.Roll());
//...
}
demo

Related

How to fill a variable in class in array of classes?

I have a class with just one variable
public class C
{
int i;
}
And in another project file I create an array of classes
C[] classes = new C[100000];
So what i need to do to set some random value to the "i" variable in each class?
First you need to make C.i accessible. One way is to make C.i a public property. While you’re at it, public fields should be pascal cased and all identifiers should have meaningful names.
When naming public members of types, such as fields, properties, events, methods, and local functions, use pascal casing.
public class Foo {
public int Bar { get; set; }
}
Then you'd use System.Random. Instantiate it once and call Random.Next each time you want a random number.
using System;
var rand = new Random();
// int anyPositiveInt = rand.Next();
// int positiveIntLessThanFifteen = rand.Next(15);
// int intFromOneToFour = rand.Next(1, 5);
Finally, following the example in Creating N objects and adding them to a list, use System.Linq's Enumerable.Range, Enumerable.Select, and Enumerable.ToArray as follows:
Foo[] classes = Enumerable
.Range(0, 100000)
.Select(_ => new Foo { Bar = rand.Next() })
.ToArray();
If the requirement is to use a private field then I recant the earlier advice to make it a public property - properties might not have been taught yet
static void Main()
{
var r = new Random();
var maxValueOfI = 100;
var minValueOfI = -20;
var csArr = new C[100000];
for (var julius = 0; julius < csArr.Length; julius++) {
var brutus = r.Next(minValueOfI, maxValueOfI+1);
csArr[julius] = new C(brutus);
}
}
public class C
{
private int _i;
public C(int i){
_i = i;
}
}
So, what's going on here?
The main addition is a constructor to C. A constructor is a special method that is called by C# when a new object is constructed. Every class has one even if you can't see it (the compiler provides one if you don't). Constructors are methods that are intended to ensure the class is fully set up and ready to use. Because it's inside the class it has full access to all the data fields of the class:
public C(int i){
_i = i;
}
This constructor takes an int, and sets the private field _i to the value of the passed in number. It's quite common to use this naming pattern for fields (prefix with underscore) and it helps avoid a name collision with the arguments to the method (in this case called i). If they had both been called i the class one would have to be prefixed with this. and it's (IMHO) more clutter
This line of code calls the constructor:
csArr[julius] = new C(brutus);
We've previously calculated a random number between -30 and 100 (inclusive both ends) and stashed it in a variable called brutus. This number is passed to the constructor, which is called when we say new C. The resulting fully constructed C instance is stored in one of the array slots
try this
static void Main()
{
Random rand = new Random();
var max=100000;
C[] array = new C[max];
for (var i=0; i <max; i++)
array[i] = new C { Num = rand.Next(0, max)};
// or using a constructor
array[i] = new C (rand.Next(0, max));
}
public class C
{
public int Num {get; set;}
public C (int num)
{
Num=num;
}
public C (){}
}
First of all, in your current code i is a private field. Let's add a constructor to set this field:
public class C {
int i;
public C(int value) {
i = value;
}
}
Then you can try using Linq:
using System.Linq;
...
Random rand = new Random();
...
C[] classes = Enumerable
.Range(0, 100000)
.Select(i => new C(rand.Next(0, 100))) //TODO: Put the right range here
.ToArray();

C# - How do I call a random number generator class to main?

So I am trying to make this wizard battle game which will heavily use Random Number Generator for plethora of things such as choosing level, name, and spells for a enemy wizard. I have generated the basic number generator but I am wondering how do I call this to Main class? Here is the code for what I have done so far. I am absolutely new to programming so I do apologize.
using System;
namespace Battle_Wizard
{
class Program
{
static void Main(string[] args)
{
string Player;
Console.WriteLine("Hello Wizard! What is your name: (Insert your name) ");
Player = Console.ReadLine();
Console.WriteLine("So your name is "+ Player + "? " + "What a stupid name. \nPRESS ANY BUTTON FOR A BATTLE!");
Console.ReadKey();
}
public class Wizards
{
string [] names = {"Ifeus","Avutaz","Alvapan","Inawyn","Agrukey","Unageor","Anvigron","Ubus","Enoviar","Unitor"};
string [] spells = {"A Alakablam ","Y0ur m0m ","A Karate Chop ","Abra-kadabra ","A 12 Gauge Shotgun ","Telekinesis "};
string [] deathmessages = {" set their pants on fire by ", " shot in the face with a ", " perished painfully with "};
}
public class NumberGenerator
{
Random rnd = new Random();
int EnemyRoll = new Random().Next( 1, 10 );
int PlayerRoll = new Random().Next( 1, 10 );
}
}
}
Here you are creating 3 instances of Random with the new keyword. It is better to use a single instance within the NumberGenerator class.
public class NumberGenerator
{
Random rnd = new Random();
int EnemyRoll = new Random().Next( 1, 10 );
int PlayerRoll = new Random().Next( 1, 10 );
}
I would rewrite this class like this:
public class NumberGenerator
{
Random rnd;
public NumberGenerator()
{
rnd = new Random();
}
// use seperate methods for each thing you want to generate
int generateEnemyRoll()
{
return rnd.Next(1, 10);
}
int generatePlayerRoll()
{
return rnd.Next(1, 10);
}
string generateDeathMessage()
{
return Wizards.deathmessages[rnd.Next(0, Wizards.deathmessages.Length)];
}
// etc for all the other things you need to generate
}
Also, on the Wizards class, you have three arrays of strings which I presume are never going to change (during runtime), so you can put static on them so they don't require an object reference to access (You don't need to create a Wizard object to access the strings). You could also just put these within the RandomGenerator class or within the generateDeathMessage() method.
Like so...
public class Wizard
{
static string [] names = {"Ifeus","Avutaz","Alvapan","Inawyn","Agrukey","Unageor","Anvigron","Ubus","Enoviar","Unitor"};
static string [] spells = {"A Alakablam ","Y0ur m0m ","A Karate Chop ","Abra-kadabra ","A 12 Gauge Shotgun ","Telekinesis "};
static string [] deathmessages = {" set their pants on fire by ", " shot in the face with a ", " perished painfully with "};
}
How to use from the main class? Main is a method not a class, but from the Main method you can do this...
static void Main(string[] args)
{
string Player;
Console.WriteLine("Hello Wizard! What is your name: (Insert your name) ");
Player = Console.ReadLine();
Console.WriteLine("So your name is "+ Player + "? " + "What a stupid name. \nPRESS ANY BUTTON FOR A BATTLE!");
NumberGenerator generator = new NumberGenerator();
int enemyRoll = generator.generateEnemyRoll();
int playerRoll = generator.generatePlayerRoll();
string deathMessage = generator.generateDeathMessage();
// etc
Console.ReadKey();
}
Your NumberGenerator class is public however, you're going to want to make that static. The reason for this is because you don't want your NumberGenerator to be able to be instantiated. What is instantiation, I'm glad you asked.
Here's an example of instantiation.
public class Car {
//CAR properties sometimes referred to as "Member Variables."
private string _color;
private int _wheels;
//Initialization method. This is what tells the compiler what goes where and it's typing.
public Car (string color, int wheels) {
_color = color;
_wheels = wheels;
}
}
public SomeClass {
//Instantiation of that car we created above.
Car someCar = new Car ("Blue", 4);
//Using the properties of that Car object.
string someColor = someCar.wheels;
int someWheelNum = someCar.wheels;
}
Next, you need to think of your class NumberGenerator as a simple file to hold other methods. Inside of a class, you can have properties, For example, you might have a static name that is accessible from outside. However since we're doing a static class, that's not necessary. We do need a method of some sort within that class.
public int getRandomNum() {
//Do something here.
return someInt
}
Finally, all you need to do is use it with dot syntax.
int someInt = NumberGenerator.getRandomNum();
I encourage you to go learn to build Class Objects as a next step. It's a precursor to what you're attempting to do. Build a class object that's a car with some computed properties.
Here's a great tutorial video for you to follow. https://www.youtube.com/watch?v=ZqDtPFrUonQ

Best way to write multiple constructor overloads in C#

I am learning C# and made a simple "Player" class. But I struggle having multiple overload.
Here's my best solution but I feel like it could be done simpler/better.
class Player : Entity
{
public Player() {
Name = "Player";
XP = 0;
LVL = 1;
XPToLvlUp = 10;
XpRank = 10;
}
public Player(string name) : this() {
Name = name;
}
public Player(string name, int _Hp, int _Mp) : this(name) {
HP = _Hp;
MP = _Mp;
}
public Player(string name, int _Hp, int _Mp, int _Xp, int _Lvl) : this(name, _Hp, _Mp) {
XP = _Xp;
LVL = _Lvl;
}
public Player(string name, int _Hp, int _Mp, int _Xp, int _Lvl, int XpByRank) : this(name, _Hp, _Mp, _Xp, _Lvl) {
XpRank = XpByRank;
}
//deleted code for better reading
private int XPToLvlUp;
private int XpRank;
public int XP;
public int LVL;
public string Name;
}
Is it good and if not please tell me why.
Thanks for your responses!
I think it's fine as is. One question to ask yourself: Are each of those methods actually likely to be called?
One option is to just let the programmer set those values after they've instantiated the class:
var myPlayer = new Player();
myPlayer.XP = 5;
However, there are situations where you really want all the info up front, so that may not be suitable.
Another option could be an options class that is passed to the ctor:
public class PlayerSettings
{
public Name = "Player";
public XP = 0;
public LVL = 1;
public XPToLvlUp = 10;
public XpRank = 10;
}
Then your ctors looks like this:
public Player() : this(new PlayerSettings())
{
}
public Player(PlayerSettings settings)
{
//Fill in appropriate variables here
}
That option would be called in this way:
var playerSettings = new PlayerSettings() { XP = 5 };
var myPlayer = new Player(playerSettings());
In the end, I'm not sure one is "better" than the other, it largely depends on your needs.
Your class is almost good and acceptable.
Short story: use Properties.
Long story:
First of all make or follow the naming rules, it will make your code more friendly to read. It's up to you, just a suggestion. For complex names consisting of multiple words you may use CamelCasedNames. And avoid shorten names for all types of data where it maybe useful. For example you may expand Lvl to Level but Xp to Experience will look as something odd. It's up to you too.
string name; // local Variable, first character lower cased
private string _name; // private Field, first character is lower cased with leading "_"
public string Name { get; set; } // public Property, first character is upper cased
I'll show you alternatives to overriden constructors and will follow the naming rules.
1) Default values for constructor (with a part of your class to keep it simple)
class Player
{
public Player(string name = "Player", int xp = 0, int level = 1)
{
Name = name;
Xp = xp;
Level = level;
}
// Properties instead of Fields
public int Xp { get; private set; } // restrict modification of the property outside of a class but reading is available
public int Level { get; private set; }
public string Name { get; set; }
}
2) Properties without constructor with default values
First Property purpose is restrict access to data to keep internal object data consistent. Even you make mistakes in the code. Good way to avoid some bugs.
Second property purpose is executing code while you're getting or setting one. For example, making properties dependent on each other to store less and only unique data.
class Player
{
public int Xp { get; private set; } = 0;
public int Level { get; private set; } = 1;
public string Name { get; set; } = "Player";
}
Usage
Player player = new Player() { Name = "KillerPWNZ", Level = 100, Xp = 999999 };
Bonus: Another Property feature
You can execute any code in get or set clause.
Let's assume that each next player's level require doubled amount of xp from previous but 2nd level requre 100 XP. And you decided to invoice to the 1st leveled player 1000 XP. Obviously you'll need to bump the Level few times. Assuming that Xp contains relative to Level value.
The invoice
player.Xp += 1000;
The Property with code
private int _xp = 0;
public int Level { get; private set; } = 1;
public int Xp
{
get => _xp; // same as: get { return _xp; }
set
{
_xp = value; // here value is keyword containing data you want to set
while (_xp >= GetXpPerLevel(Level))
{
_xp -= GetXpPerLevel(Level);
Level++;
}
while (_xp < 0 && Level > 1)
{
_xp += GetXpPerLevel(Level - 1);
Level--;
}
}
}
// helper method
private int GetXpPerLevel(int level)
{
if (level < 1) return 0;
// int result = 100;
// for (int i = 1; i < level; i++) result *= 2;
// return result;
// or the same with some binary shift magic :)
return 100 << (level - 1);
}

c# array looping with itself

Why I'm doing this:
So I'm trying to make an application for a game called clash royale, after winning the games there you get a "random" chest, which is actually not random... When you create your account you get a digit assigned to you from 0 to 239, and after that it follows a pattern for the chest drops. The applciation I'm making would take a user's entries and compare it to the pattern, thus being able to predict how soon the next chests of a higher quality would drop.
The help I need with the code:
Is it possible to make an array kind of... loop within itself.. So for example when going through the array in a loop, if "i" is 239, then adding +1 would take it back to the beginning, or #0 (239 not necessarily being the limit).
The class (and it's container that I want to loop):
class Chest
{
public int ID { get; set; }
public string Type { get; set; }
public Chest()
{
}
public Chest(int id, string type)
{
ID = id;
Type = type;
}
}
class ChestContainer
{
private Chest[] ChestList = new Chest[240];
public int Count { get; set; }
public ChestContainer(int size)
{
ChestList = new Chest[size];
}
public void Add(Chest chest)
{
ChestList[Count++] = chest;
}
public Chest Get(int index)
{
return ChestList[index];
}
}
Also wouldn't mind any tips to improve my class / container class, at the moment this is what I've been doing for pretty much my entire "career" as this is what we were thought in uni (minus the string override for the class).
You could use Modulo % in order to get a loop kind of thing.
If you replace the Container.Add method with the one below, the index will be "reset" (for lack of better words).
public void Add(Chest chest)
{
ChestList[Count++%(ChestList.Length)] = chest;
}
After updating the method, if you want an example, you can try the code below:
var container = new ChestContainer(240);
for (int i = 0; i < 1000; i++)
container.Add(new Chest(i, $"{i}"));
Edit In order to have the Get method working as well, modifying it as mentioned below will ensure your container works as expected:
public Chest Get(int index)
{
return ChestList[index%(ChestList.Length)];
}
To test it out, you can use the code below:
var container = new ChestContainer(240);
for (int i = 0; i < 1000; i++)
{
container.Add(new Chest(i, $"{i}"));
var value = container.Get(i);
}
You can overload the [] operator to define it's behaviour.
Something like this:
public static Chest operator [] (int index) {
return ChestList[index%240];
}
public Chest Get(int index)
{
return ChestList[index%240]; //put your limit here
}
How it works: % is the modulo operator.
It returns the remainder of a devision.
Example:
5/2 = 2, remaining 1
=> 5%2 = 1
In your case, when numbers higher than 239 are entered, with modulo it just wraps around.

Use property value in another property setter

In my Class i need to set one property value according to another:
public class Quantities
{
private int _quant;
public int Quant
{
get { return _quant; }
set
{
if (Unit == "K")
{
_quant = value / 1000;
}
else
{
_quant = value;
}
}
}
public string Unit { get; set; }
}
according to several tests i made it works fine but i still don't know if it's safe to do this.
is it possible that the Quant Property will be evaluated before the Unit Property or does the compiler (or JIT) knows that it should assign the Unit Property first?
This has nothing to do with the compiler or the JIT. Your code assigns the values. You need to know the order in which they should be assigned.
BTW: Your code exhibits temporal coupling. It would be better to make at least the Unit unchangeable by making the property readonly and by providing a constructor that requires the unit:
public class Quantities
{
private readonly string _unit;
private int _quant;
public Quantities(string unit)
{
if(unit == null) throw new ArgumentNullException("unit");
_unit = unit;
}
public int Quant
{
get { return _quant; }
set
{
if (Unit == "K")
{
_quant = value / 1000;
}
else
{
_quant = value;
}
}
}
public string Unit { get { return _unit; } }
}
This class now can't be used in an incorrect way.
For more points that can be improved with your class, please refer to Lasse's answer.
Code on the outside of this class must know about this dependency or you risk someone changing Unit without re-setting Quant:
var x = new Quantities(); // why no constructor for this?
x.Unit = "K";
x.Quant = 1700; // why int? this will now be 1, not 1.7
x.Unit = "M";
Personally I would make the class a struct, and make it immutable:
public struct Quantity
{
private readonly double _Value;
private readonly string _Unit;
public Quantity(double value, string unit)
{
_Value = value;
_Unit = unit;
}
public double Value
{
{
return _Value;
}
}
public double Unit
{
{
return _Unit;
}
}
}
Also note that I did not change the value at all, hence:
var x = new Quantity(1700, "K");
means 1700K, not 1.7K. I would refrain from doing such "automagical" interpretations of data. If you need to display the value with a different unit, I would instead build in a conversion system:
public Quantity ConvertToUnit(string newUnit)
{
var newValue = ... calculate value with new unit
return new Quantity(newValue, newUnit);
}
The class is not a good design. Do not do this.
Consider the following code:
Quantities q1 = new Quantities { Unit = "K", Quant = 1000};
Console.WriteLine(q1.Quant); // Prints 1
// Make a copy of q1
Quantities q2 = new Quantities{ Unit = q1.Unit, Quant = q1.Quant };
Console.WriteLine(q2.Quant); // Prints 0
You would expect that making a copy of the Quantities would work by doing a basic copy like the above. That it does not shows you how dangerous this kind of design is.
This is still a problem after making the changes in the accepted answer above
If you use the changes that Daniel suggested, you still have the nastyness associated with your property setter and getter not being commutative. Sure, you would be forced to pass the units into the constructor, but the object copy still won't work as the user might expect:
Quantities q1 = new Quantities("K"){ Quant = 1000};
Console.WriteLine(q1.Quant); // Prints 1
// Make a copy of q1
Quantities q2 = new Quantities(q1.Unit){ Quant = q1.Quant };
Console.WriteLine(q2.Quant); // STILL Prints 0

Categories