Is range variable possible in C# (.NET) - c#

I'm looking for some class or data type to allow me define a range for it.
This is the pseudo code which I'm looking for :
[Range(0, 75)] int ChildAge;
This range rule should be applied to any int which this Age is assigned like following
var MotherAge = ChildAge;
MotherAge = 100;
MotherAge should be set to 75 and this is what I'm looking for.

Another option would be to define your own data type to represent age values. This type could enforce the constraints.
struct Age
{
const int MinAge = 0;
const int MaxAge = 75;
readonly byte value;
public int Value { get { return value; } }
private Age(int value) {
this.value = (byte) Math.Max(MinAge, Math.Min(MaxAge, value));
}
public static implicit operator Age(int value) {
// Throw here if value is out of range, maybe?
return new Age(value);
}
public static implicit operator int(Age age) {
return age.value;
}
}
//usage:
Age childAge = 12; // 12
Age motherAge = 100; // 75
Edit: I would point out that it is generally considered bad practice to have "lossy" conversions exposed as implicit casts. I should have made the operator int(Age) conversion explicit instead. This would require writing an explicit cast Age age = (Age) 100; which advertises to the consumer of the API that the cast isn't "identity preserving". This is similar to a cast from long to int or double to float, there is a loss of range/precision so the language requires you be explicit about it to demonstrate that you understand what you are doing.

There is no such thing in C#, but you could create a class that handles it easily:
public class Age
{
public Age(int age) : this(0, 75, age) { }
public Age(int minAge, int maxAge) : this(minAge, maxAge, minAge) { }
public Age(int minAge, int maxAge, int age)
{
this._Minimum = minAge;
this._Maximum = maxAge;
this.Value = age;
}
private int _Value = 0;
public int Value
{
get
{
return _Value;
}
set
{
CheckRange(value, true);
}
}
private int _Maximum = 0;
public int MaximumAge
{
get
{
return _Maximum;
}
set
{
if (value < _Minimum)
throw new ArgumentException("MaximumAge");
_Maximum = value;
CheckRange(value, false);
}
}
private int _Minimum = 0;
public int MinimumAge
{
get
{
return _Minimum;
}
set
{
if (value > _Maximum)
throw new ArgumentException("MinimumAge");
_Minimum = value;
CheckRange(value, false);
}
}
private void CheckRange(int value, bool setValueAnyway)
{
if (value < _Minimum)
_Value = _Minimum;
else if (value > _Maximum)
_Value = _Maximum;
else if (setValueAnyway)
_Value = value;
}
}
Now your sample ages:
Age childAge = new Age(0,75);
Age motherAge = childAge;
motherAge.Value = 100; // 75

You don't need an attribute. You can use a property:
private int _age;
public int ChildAge
{
get { return _age; }
set
{
if(value > 75)
_age = 75;
else if(value < 0)
_age = 0;
else
_age = value;
}
}

Related

How can I get and set the Range attribute min and max?

[Range(-3, 3)]
public float range;
I want later in the code to do something like :
range.min = 2
range.max = 20;
Or get
int min = range.min;
int max = range.max;
You can't change an attribute's state at runtime, but you can read it using reflection:
class YourClass
{
[Range(-3, 3)]
public float range;
}
var range = typeof(YourClass)
.GetField(nameof(YourClass.range))
.GetCustomAttribute<RangeAttribute>();
float min = range.min;
float max = range.max;
Based on: https://github.com/jamesjlinden/unity-decompiled/blob/master/UnityEngine/UnityEngine/RangeAttribute.cs
Can you change it to an object like below?
public class range
{
private float _value;
public range(int min, int max)
{
Min = min;
Max = max;
}
public float Value
{
get
{
return _value;
}
set
{
if (value > Max || value < Min) throw new Exception("value out of
range.");
_value = value;
}
}
public int Min { get; }
public int Max { get; }
}
use it like;
rangeObj = new range(-3,3);
rangeObj.Min
rangeObj.Max
rangeObj.Value

Selecting a random object from a list of objects

I'm trying to randomly select one of the customer items from the list. I am not sure what to do to actually print out the info within the list.
I have this as my customer class
namespace PizzaParlor
{
class Customer
{
private string name;
private int flavor;
private int price;
private int quality;
private int speed;
private int accessibility;
private int brand;
private int convenience;
private int variety;
public Customer(string name, int flavor, int price, int quality, int speed, int accessibility, int brand, int convenience, int variety)
{
this.name = name;
this.flavor = flavor;
this.price = price;
this.quality = quality;
this.speed = speed;
this.accessibility = accessibility;
this.brand = brand;
this.convenience = convenience;
this.variety = variety;
}
// Name, Speed, Quality, Variety, Convenience, Accessibility, price, brand, flavor
public string Name
{
get { return name; }
set { name = value; }
}
public int Speed
{
get { return speed; }
set { speed = value; }
}
public int Quality
{
get { return quality; }
set { quality = value; }
}
public int Variety
{
get { return variety; }
set { variety = value; }
}
public int Convenience
{
get { return convenience; }
set { convenience = value; }
}
public int Accessibility
{
get { return accessibility; }
set { accessibility = value; }
}
public int Price
{
get { return price; }
set { price = value; }
}
public int Brand
{
get { return brand; }
set { brand = value; }
}
public int Flavor
{
get { return flavor; }
set { flavor = value; }
}
}
}
and this as my main class that I set up to work with the customer class:
namespace PizzaParlor
{
class Program
{
static void Main(string[] args)
{
var random = new Random();
List<Customer> CustomerList = new List<Customer>();
CustomerList.Add(new Customer("bill", 20,15,10,5,10,20,5,15));
CustomerList.Add(new Customer("kevin", 15, 10, 5, 20, 15, 15, 0, 20));
CustomerList.Add(new Customer("clair", 8,25,2,25,5,15,0,20));
CustomerList.Add(new Customer("jim", 15,20,10,15,0,40,0,0));
CustomerList.Add(new Customer("rachel", 20,15,10,5,10,30,0,10));
CustomerList.Add(new Customer("jeff", 30,20,5,5,10,10,0,20));
CustomerList.Add(new Customer("Mike", 21,23,0,10,14,16,0,16));
CustomerList.Add(new Customer("john", 25,15,10,10,10,5,5,20));
int index = random.Next(CustomerList.Count);
Console.WriteLine(CustomerList[index]);
}
}
}
I know that the random.Next(CustomerList.Count) will randomly a select from the list but I don't know why it is returning this output:
This is because the when you attempt to print an object (e.g. Customer, the default implementation of ToString() is executed. This produces the output that you see.
There are 2 ways of fixing it
Print explicit fields you're interested in
int index = random.Next(CustomerList.Count);
var customer = CustomerList[index];
Console.WriteLine($"customer name = {customer.Name}, flavour = {customer.Flavour}}");
Override the ToString implementation
class Customer
{
//...
// Existing code
// ..
public override string ToString ()
{
return $"customer name = {customer.Name}, flavour = {customer.Flavour}}";
}
}
In your main method
int index = random.Next(CustomerList.Count);
var customer = CustomerList[index];
Console.WriteLine(customer);
You can use Reflection to achieve this.
foreach (var prop in typeof(Customer).GetProperties())
{
Console.WriteLine("{0}={1}", prop.Name, prop.GetValue(CustomerList[index]));
}

How to generalize a property pattern

I have classes that has multiple properties which have well-defined name and function but have the same implementation. For example:
class Stats
{
private int attack;
public int Attack
{
get =>
HasBuff ? attack + 1 : attack;
set
{
if (value < 1 || value > 10)
throw new ArgumentOutOfRangeException("Invalid value");
attack = value;
}
}
public int Defense {...}
public int Speed {...}
}
Where Defense and Speed are to be implemented just like Attack . How can I generalize this structure to avoid redundancy and make changes easier?
Make another class to generalize stats:
public class Stat
{
public bool HasBuff { get; set; }
private int _stat;
public int Score
{
get => HasBuff ? _stat + 1 : _stat;
set => _stat = value;
}
}
Then just use that for each of your skills:
public class CombatStats
{
public Stat Attack { get; } = new Stat();
public Stat Defense { get; } = new Stat();
public Stat Speed { get; } = new Stat();
}
Calling code would look like this:
var ninja = new Ninja();
ninja.skills = new CombatStats();
var attackStrength = ninja.skills.Attack.Score;
As further improvement, implicit operators can be used to avoid object creation and call to Score:
public class Stat
{
...
public static implicit operator int(Stat stat)
{
return stat.Score;
}
public static implicit operator Stat(int value)
{
return new Stat()
{
Score = value
};
}
}
This makes the change transparent to client code written w.r.t. to the example in the question:
ninja.skills = new CombatStats(){
Attack = 5,
Defense = 2
}
int attack = ninja.skills.Attack;
One approach to consider:
class Stats
{
// other existing code here
private int defense;
public int Defense
{
get
{
return GetValue(defense);
}
set
{
SetValue(value, ref defense);
}
}
private int GetValue(int value)
{
return HasBuff ? value + 1 : value;
}
private void SetValue(int value, ref int target)
{
if (value < 1 || value > 10)
throw new ArgumentOutOfRangeException("Invalid value");
target = value;
}
}
Attack etc will now be basically the same as Defence but passing in attack rather than defense to GetValue and SetValue.
I would go with composition
Stat:
public class Stats
{
private readonly StatProperty _defense;
private readonly StatProperty _attack;
private readonly StatProperty _speed;
public Stats()
{
_defense = new StatProperty(this);
_attack = new StatProperty(this);
_speed = new StatProperty(this);
}
public int Defense
{
get => _defense.Value;
set => _defense.Value = value;
}
public int Attack
{
get => _attack.Value;
set => _attack.Value = value;
}
public int Speed
{
get => _speed.Value;
set => _speed.Value = value;
}
public bool HasBuff { get; set; }
}
StatProperty:
public class StatProperty
{
public Stats Stats { get; }
public StatProperty(Stats stats)
{
Stats = stats;
}
private int _value = 1;
public int Value
{
get => Stats.HasBuff ? _value + 1 : _value;
set
{
if (value < 1 || value > 10)
throw new ArgumentOutOfRangeException("Invalid value");
_value = value;
}
}
}
I would need more details to know if it is the best option.
you also could make StatProperty as internal if don't want to show it outside of your library or nested private class if you want to use this just on the class Stats

Trouble setting my Setter using console.readline();

I'm trying to figure out how to set my Setter via the readline from the console. Any help would be wonderful.
MathOperations toDo = new MathOperations();
Console.WriteLine("Enter first number to calculate");
toDo.inputOne = int.Parse(Console.ReadLine());
Console.WriteLine("Enter second number to calculate");
toDo.inputTwo = int.Parse(Console.ReadLine());
Console.WriteLine("Added: " + toDo.addNumbers(value1, value2));
class MathOperations
{
private int inputOne;
private int inputTwo;
public int getInputOne()
{
return inputOne;
}
public void setInputOne(int value)
{
inputOne = value;
}
public int getInputTwo()
{
return inputTwo;
}
public void setInputTwo(int value)
{
inputTwo = value;
}
public int addNumbers(int number1, int number2)
{
int total;
total = number1 + number2;
return total;
}
}
You want a Proprety:
public int InputOne //bad name
{ get; set; }
This is an example of an auto-property
You can also do:
private int inputTwo;
public int InputTwo //bad name
{
get
{
return inputTwo;
}
set
{
inputTwo = value;
}
}
With one of those, the code you have will call the "set" function as you expect by writing:
InputOne = int.Parse(Console.ReadLine());
InputTwo = int.Parse(Console.ReadLine());
To address the comment about naming, in this case I would use List<int> Operands as the property, and have the readline commands add to this list. Then the addNumbers function just becomes:
return Operands.Sum();
This makes it much easier to add 3, 4, or even more numbers in the future without having to create more variables, etc. You could even read the inputs in a loop with this method.
You don't have a property with a setter/getter you just use set-/getMethods. You need to have a property like this:
public int InputOne {
set { inputOne = value; }
get { return inputOne; }
}
public int InputTwo {
set { inputTwo = value; }
get { return inputTwo; }
}
Using Auto-Implemented Properties
class MathOperations
{
private int inputOne {get;set;}
private int inputTwo {get;set;}
public int addNumbers(int number1, int number2)
{
int total;
total = number1 + number2;
return total;
}
}

C# get/set inaccessibility error

I am getting an error as followed:
Inconsistent accessibility: property type 'AudioDevices.Tracks.track.Time' is less accessible than property 'AudioDevices.Tracks.track.length'
I have no clue what it is, or how i can fix it. Anybody that can help me?
This is all the code i have, [template = class library]:
namespace AudioDevices.Tracks
{
public class Track
{
#region STRUCT
private int id;
private string name;
private string artist;
private string albumSource;
private Time length;
private category style;
public enum category{
Ambient, Blues, Country, Disco, Electro, Hardcore, HardRock, HeavyMetal, Hiphop, Jazz, Jumpstyle,
Klassiek, Latin, Other, Pop, Punk, Reggae, Rock, Soul, Trance, Techno
};
#endregion
#region GET/SET
public int Id{
get { return id; }
set { id = value; }
}
public string Name{
get { return name; }
set { name = value; }
}
public string Artist{
get { return artist; }
set { artist = value; }
}
public string AlbumSource{
get { return albumSource; }
set { albumSource = value; }
}
public Time Length{
set { length = value; }
}
public string DisplayTime
{
get { return length.ToString(); }
}
public category Style
{
get { return style; }
set { style = value; }
}
#endregion
#region TIME CONSTRUCTOR
struct Time
{
int seconds;
int minutes;
int hours;
public Time(int seconds)
{
this.seconds = seconds;
this.minutes = 0;
this.hours = 0;
}
public Time(int seconds, int minutes)
{
this.seconds = seconds;
this.minutes = minutes;
this.hours = 0;
}
public Time(int seconds, int minutes, int hours)
{
this.seconds = seconds;
this.minutes = minutes;
this.hours = hours;
}
public override string ToString()
{
return hours + ":" + minutes + ":" + seconds;
}
}
#endregion
#region TRACK CONSTRUCTOR
public Track(){ }
public Track(int id)
{
this.id = id;
}
public Track(int id, string name)
{
this.id = id;
this.name = name;
}
public Track(int id, string name, string artist)
{
this.id = id;
this.name = name;
this.artist = artist;
}
#endregion
#region GetLength
public string GetLength()
{
return length.ToString();
}
public int GetLengthInSeconds(int seconds, int minutes, int hours){
int SecondsToSeconds = seconds;
int MinutesToSeconds = minutes * 60;
int HoursToSeconds = hours * 3600;
int TotalSeconds = HoursToSeconds + MinutesToSeconds + SecondsToSeconds;
return TotalSeconds;
}
#endregion
}
}
You've got a public property here:
public Time Length{
set { length = value; }
}
... but the type of that property is Time, which is a private type:
struct Time {
...
}
(It's private because it's a nested type; if it were declared as a top-level type it would be internal by default, which would still have the same problem.)
Public member signatures can't refer to private or internal types anywhere in the parameter types or return type. The member simply wouldn't be meaningful to the caller if they were in a different assembly.
So, the fix is to either make Time a public type (and I'd recommend extracting it as a top-level type at the same time) or to make Time a private property.
From MSDN;
The access level for class members and struct members, including
nested classes and structs, is private by default.
So, your Time struct is private by default.
On this part;
public Time Length
{
set { length = value; }
}
You are trying to create public property the type of private struct. You can't do that. For fixing it,
Change your Length property access modifier public to private.
or
Set your Time struct access modifier to public.
This might be because you are using a construct Time.
Try to alter your code like:
public Time Length{
set { length = new Time(value); }
}

Categories

Resources