[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
Related
The float word I think is inappropriate but...
I try to make a custom slider control and seems to work fine but for some reason for some values the control crashes.
For example if I choose Maximum value = 1.112 the slider show maximum 1.111 and if I increase the value with numeric Up Down I get this error:
System Argument Out Of Range Exception H Result = 0x80131502
Message=Value of '1112' is not valid for 'Value'. 'Value' should be
between 'Minimum' and 'Maximum'.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace UserSliderControl
{
//set the default event for this user control
[DefaultEvent("ValueChanged")]
public partial class SliderControl : UserControl
{
//set the initial values
private int multiplier = 1;
private float minimum = 0f;
private float maximum = 100f;
private int decimalPlaces = 0;
private float _value = 0f;
private float resetvalue = 0f;
private int largeChange = 5;
private Image buttonResetImage = null;
private Color controlNameForeColor = SystemColors.WindowText;
//set the event handler for this user control = ValueChanged
public event EventHandler ValueChanged;
//set the properties for this user control
#region properties
public string ControlName
{
get { return labelControlName.Text; }
set
{
labelControlName.Text = value;
}
}
public Color ControlNameForeColor
{
get { return controlNameForeColor; }
set
{
controlNameForeColor = value;
labelControlName.ForeColor = controlNameForeColor;
}
}
public Image ButtonResetImage
{
get { return buttonResetImage; }
set
{
buttonResetImage = value;
btnReset.BackgroundImage = buttonResetImage;
btnReset.BackgroundImageLayout = ImageLayout.Zoom;
}
}
public float Value
{
get { return _value; }
set
{
_value = value;
if (_value > maximum) _value = maximum;
if (_value < minimum) _value = minimum;
NumericUpDown.Value = (decimal)_value;
}
}
public float ResetValue
{
get { return resetvalue; }
set
{
resetvalue = value;
if (resetvalue > maximum) resetvalue = maximum / 2;
if (resetvalue < minimum) resetvalue = minimum;
}
}
public float Maximum
{
get { return maximum; }
set
{
maximum = value;
if (maximum < minimum)
{
maximum = 2 * minimum;
}
TrackBar.Maximum = (int)(maximum * multiplier);
NumericUpDown.Maximum = (decimal)maximum;
}
}
public float Minimum
{
get { return minimum; }
set
{
minimum = value;
if (minimum > maximum)
{
minimum = 0;
}
TrackBar.Minimum = (int)minimum * multiplier;
NumericUpDown.Minimum = (int)minimum;
}
}
public int DecimalPlaces
{
get { return decimalPlaces; }
set
{
decimalPlaces = value;
if (decimalPlaces > 3)
{
decimalPlaces = 3;
}
multiplier = (int)Math.Pow(10, decimalPlaces);
NumericUpDown.DecimalPlaces = decimalPlaces;
NumericUpDown.Increment = 1m / multiplier;
}
}
public int LargeChange
{
get { return largeChange; }
set
{
largeChange = value;
TrackBar.LargeChange = largeChange;
}
}
#endregion
public SliderControl()
{
InitializeComponent();
}
private void TrackBar_Scroll(object sender, EventArgs e)
{
NumericUpDown.Value = (decimal)TrackBar.Value / multiplier;
Value = (float)TrackBar.Value / multiplier;
OnValueChanged();
}
private void NumericUpDown_ValueChanged(object sender, EventArgs e)
{
TrackBar.Value = (int)(NumericUpDown.Value * multiplier);
Value = (float)NumericUpDown.Value;
OnValueChanged();
}
private void btnReset_Click(object sender, EventArgs e)
{
NumericUpDown.Value = (decimal)ResetValue;
TrackBar.Focus();
OnValueChanged();
}
protected virtual void OnValueChanged()
{
ValueChanged?.Invoke(this, EventArgs.Empty);
}
}
}
This is the static array I have been given in making a RPN calculator. From this code the RPN calculator adds and subtracts. Now I need to extend my code to multiply and divide but I cant I don't know how.
public class IntStack
{
private const int maxsize = 10;
private int top = 0;
private int[] array = new int[maxsize];
public void Push(int value)
{
array[top++] = value;
}
public int Pop()
{
return array[--top];
}
public int Peek()
{
return array[top - 1];
}
public bool IsEmpty()
{
return top == 0;
}
public bool IsFull()
{
return top == maxsize;
}
public string Print()
{
StringBuilder output = new StringBuilder();
for (int i = top - 1; i >= 0; i--)
output.Append(array[i] + Environment.NewLine);
return output.ToString();
}
}
Here are some methods you can add to your IntStack class that will perform the multiply and division operations. I've added minimal error checking.
public void Multiply()
{
if (array.Length < 2)
return;
var factor1 = Pop();
var factor2 = Pop();
Push(factor1 * factor2);
}
public void Divide()
{
if (array.Length < 2)
return;
var numerator = Pop();
var divisor = Pop();
if (divisor == 0) { // Return stack back to original state.
Push(divisor);
Push(numerator);
return;
}
Push(numerator / divisor);
}
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
This is the first class:
public class TextBoxInt : TextBox
{
public int min;
public int max;
public Value<int> value;
public virtual void Update(object sender, EventArgs e)
{
int newValue;
if (int.TryParse(Text, out newValue))
{
if (newValue < min || newValue > max)
{
//do thing A
}
value.Set(newValue);
Text = value.Get().ToString();
}
else
{
Text = value.Get().ToString();
Focus();
}
}
public TextBoxInt(Value<int> value, int min, int max)
{
this.value = value;
this.min = min;
this.max = max;
Text = value.Get().ToString();
LostFocus += new EventHandler(update);
}
}
This is the second class:
public class TextBoxFloat : TextBox
{
public float min;
public float max;
public Value<float> value;
public virtual void Update(object sender, EventArgs e)
{
float newValue;
if (float.TryParse(Text, out newValue))
{
if (newValue < min || newValue > max)
{
//do thing A
}
value.Set(newValue);
Text = value.Get().ToString();
}
else
{
Text = value.Get().ToString();
Focus();
}
}
public TextBoxFloat(Value<float> value, float min, float max)
{
this.value = value;
this.min = min;
this.max = max;
Text = value.Get().ToString();
LostFocus += new EventHandler(update);
}
}
Also, this is the Value class :
public class Value<T>
{
private T value;
private List<IValueListener<T>> listeners = new List<IValueListener<T>>();
public Value(T value)
{
this.value = value;
}
public T Get()
{
return value;
}
public void Set(T value)
{
this.value = value;
foreach (IValueListener<T> listener in listeners)
{
listener.ValueUpdated(this);
}
}
public void AddListener(IValueListener<T> listener)
{
listeners.Add(listener);
}
public void RemoveListener(IValueListener<T> listener)
{
listeners.Remove(listener);
}
}
As you can see, the first two classes are basically the same class. The only difference is the type. First one is int, the other one float. It seems that I would make for nicer code if I could combine those two into a single class.
I can set min and max to be floats and just cast them to int when needed if it's an int class. I'd just make sure I pass "whole" floats when the type is int.
Is there any way I can do it without duplicating Update() method (If int do codeForInt, else if float do sameCodeButForFloat)?
Also, even if I do duplicate the code, I run into a problem with value.Set(newValue); - in one case newValue would be int, in other it would be float, and I can't cast either to T.
Also, is there a way to limit the generic type? To specify it can only be int or a float?
Should I just leave them as two classes, or is there a way to unify them?
Instead of making separate classes, you could make a generic class.
public class BoundedTextBox<T> : TextBox where T : IComparable<T> ...
Declaring that T implements IComparable<T> will allow you to check if T is in bounds during your set operation.
if (newValue.CompareTo(min) <= 0 || newValue.CompareTo(max) >= 0)
{
// do thing A
}
What about having an abstract TextBox<T> class inheriting from the TextBox class, where TextBox<T> has a new abstract string GetValue() method? You'll have TextBoxFloat class implementing GetValue() which will do the float specific logic and similarly will the TextBoxInt class. Your TextBox<T> would be something like
public abstract class TextBox<T> : TextBox
{
public T min;
public T max;
public Value<T> value;
public virtual void Update(object sender, EventArgs e)
{
Text = GetValue();
Focus();
}
public TextBoxFloat(Value<T> value, T min, T max)
{
this.value = value;
this.min = min;
this.max = max;
Text = value.Get().ToString();
LostFocus += new EventHandler(update);
}
public abstract string GetValue();
}
As #flkes stated, a generic class is the way to go on this one. You could try something along these lines: (you can find a fine example Here)
public abstract class TextBoxBase
{
public abstract object GetMin();
public abstract object GetMax();
public abstract object GetValue();
}
public abstract class TextBox<T> : TextBoxBase
{
public T min { get; set; }
public T max { get; set; }
public T value { get; set; }
public virtual void SetTextBox(T mn, T mx, T val)
{
min = mn;
max = mx;
value = val;
}
public override object GetMin() { return min; }
public override object GetMax() { return max; }
public override object GetValue() { return value; }
}
public class TextBoxInt : TextBox<int>
{
public override void SetTextBox(int mn, int mx, int val)
{
min = mn;
max = mx;
value = val;
}
}
public class TextBoxFloat : TextBox<float>
{
public override void SetTextBox(float mn, float mx, float val)
{
min = mn;
max = mx;
value = val;
}
}
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;
}
}