MinValue & MaxValue attribute for properties - c#

Is it possible to make attribute which can limit minimum or maximum value of numbers.
Example:
[MinValue(1), MaxValue(50)]
public int Size { get; set; }
and when i do Size = -3; value of Size must be 1.
I searched in Google and can't find single example about this behavior, maybe because it is not possible to make?
I'm gonna use these attributes in property grid therefore having automatic validation can be handy.
Currently I workaround like this to limit minimum value:
private int size;
[DefaultValue(8)]
public int Size
{
get
{
return size;
}
set
{
size = Math.Max(value, 1);
}
}
So this acts like MinValue(1)

Although it is possible to create a custom attribute, attributes are just metadata for the member they annotate, and cannot change its behavior.
So, you won't get the behavior you want with a plain attribute. You need something to process the attributes in order to enact the desired behavior.
Take a look at TypeConverters for a possibility.

Yes, it is possible. Read about custom attributes at MSDN.
And by the way, there is already a solution you can use. It is the RangeAttribute which lets you specify the numeric range constraints for the value of a data field. Read more about it on MSDN.

You can elegantly solve this problem by using PostSharp by writing simple aspect, free version will be enough for this purpose:
[Serializable]
class RangeAttribute : LocationInterceptionAspect
{
private int min;
private int max;
public RangeAttribute(int min, int max)
{
this.min = min;
this.max = max;
}
public override void OnSetValue(LocationInterceptionArgs args)
{
int value = (int)args.Value;
if (value < min) value = min;
if (value > max) value = max;
args.SetNewValue(value);
}
}
and then exacly as you want:
class SomeClass
{
[Range(1, 50)]
public int Size { get; set; }
}
with normal usage:
var c = new SomeClass();
c.Size = -3;
Console.Output(c.Size);
will output 1.

create an extension
public static class Extensions
{
public static int FixedValue(this int value, int min, int max)
{
if (value >= min && value <= max)
return value;
else if (value > max)
return max;
else if (value < min)
return min;
else return 1;
}
}
And then:
private int size;
public int Size { get { return size.FixedValue(1, 50); }
set { size = value.FixedValue(1, 50); } }

Yes, it is possible with (already pointed) CustomAttributes, but mind, that you will loose the comfort of the auto-properties - because you need to apply resp. check the attribute restriction somewhere and in this case an appropriate place would be the getter of the property, so the interesting part of the problem is the application of the attributes. You can read how to access custom attributes in this MSDN article.
A possible solution for the MaxValue custom attribute can look like this:
// simple class for the example
public class DataHolder
{
private int a;
[MaxValue(10)]
public int A
{
get
{
var memberInfo = this.GetType().GetMember("A");
if (memberInfo.Length > 0)
{
// name of property could be retrieved via reflection
var mia = this.GetType().GetMember("A")[0];
var attributes = System.Attribute.GetCustomAttributes(mia);
if (attributes.Length > 0)
{
// TODO: iterate over all attributes and check attribute name
var maxValueAttribute = (MaxValue)attributes[0];
if (a > maxValueAttribute.Max) { a = maxValueAttribute.Max; }
}
}
return a;
}
set
{
a = value;
}
}
}
// max attribute
public class MaxValue : Attribute
{
public int Max;
public MaxValue(int max)
{
Max = max;
}
}
The sample usage:
var data = new DataHolder();
data.A = 12;
Console.WriteLine(data.A);
creates the output:
10
The code for the MinValue will look the same as for the MaxValue but the if condition will be less than instead of greater than.

Related

My IF property is being ignored in my class when i use it

public class Irritante : Child
{
/*Fields*/
private int ir_numeroBirras;
private double ir_mediaBirras;
/*Properties*/
public int NumeroBirras
{
get { return ir_numeroBirras; }
set { if (value > 0) ir_numeroBirras = value; }
}
public double MediaBirras
{
get { return ir_mediaBirras; }
set { ir_mediaBirras = value; }
}
//Constructor
public Irritante(string nome, int idade, int numBirras, double mediaDasBirras) : base(nome, idade)
{
NumeroBirras = numBirras;
ir_mediaBirras = mediaDasBirras;
}
When i try to use the contructor Irritante with the property NumeroBirras it is ignoring the if(value>0)
This means i can still add a 0 to this field with client code, which i should not be able to, any tips? i cant find it anywhere
The default value of ir_numeroBirras is 0. You can't put a 0 using the property. But if you test using a 0 as parameter value, you are being fooled by the default value.
If you're talking about you shouldn't put a 0 in the parameter of Irritante ctor, that's quite different
public Irritante(string name, int idade, int numBirras, double mediaDasBirras) : base(nome, idade)
{
if(numBirras < 1) throw new ArgumentOutOfRangeException(nameof(numBirras), "Hey, you can't drink 0 beers");
ir_numeroBirras = numBirras;
ir_mediaBirras = mediaDasBirras;
}

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);
}

The get and set property appears to be ignored (C#)

I'm trying to modify the "set" portion, but these changes don't seem to take place at all. Here is the basic code that shows the same results:
class Class1
{
private int num;
public Class1(int number)
{
num = number;
}
public int getNumber
{
get
{
return num;
}
set
{
if (value > 0)
num = value;
else
num = 0;
}
}
}
In here, I want to make any negative value a 0.
class Program
{
static void Main(string[] args)
{
Class1 c1 = new Class1(10);
Class1 c2 = new Class1(-10);
Console.WriteLine(c1.getNumber);
Console.WriteLine(c2.getNumber);
Console.ReadLine();
}
}
The result gives me
10
-10
I've tried using
set
{
num = 100;
}
yet there's still no change in the results. I've tried double checking with the book I'm using, and there's no difference that I can see. I'm using Visual Studio 2012 if it means anything.
Your code is not calling set portion of your property. Because, you are only calling constructor. And in your constructor you are only setting tha value for the backing field variable(num).
Also, by convention, your class members names are not appropriate.
Change it as:
num -> number getNumber -> Number
Try this:
Class1 c1 = new Class1();
c1.Number = -10; // The set accessor is invoked here
int myNumber = c1.Number; // The get accessor is invoked here
If you want to invoke set accessor through your constructor, then change your constructor as:
public Class1(int number)
{
Number = number;
}
Then it will invoke set accessor properly:
Class1 c1 = new Class1(10); // The set accessor will be invoked
And don't forget to change your class implementation as:
class Class1
{
private int number;
public int Number
{
get { return number; }
set
{
if (value > 0)
number = value;
else
number = 0;
}
}
// If you do provide a constructor (any constructor with any signature),
// the parametrless constructor will not be generated
public Class1()
{
}
public Class1(int number)
{
Number = number;
}
}
Read this from msdn for additional information.
Try this:
class Class1
{
private int num;
public Class1(int number)
{
Number = number;
}
public int Number
{
get
{
return num;
}
set
{
if (value > 0)
num = value;
else
num = 0;
}
}
}
You hadn't implented it correctly. Actually a better implmentation would be the following:
class Class1
{
// The backing field has the same name as the Property
// but all letters must be lowercase.
private int number;
public int Number
{
get { return number; }
set
{
if (value > 0)
number = value;
else
number = 0;
}
}
// In the constructor we set the value of the backing fields
// using the corresponding properties.
public Class1(int number)
{
Number = number;
}
}
The each time you want to set the value of number or get it's value you use the corresponding property:
// Set the value 2 to the number
Number = 2;
// Read the value stored in number and assigned to value.
var value = Number;

How to validate dependent parameters in PowerShell cmdlet

What's the best way to do PowerShell cmdlet validation on dependent parameters? For example, in the sample cmdlet below I need to run validation that Low is greater than High but that doesn't seem to be possible with validation attributes.
[Cmdlet(VerbsCommon.Get, "FakeData")]
public class GetFakeData : PSCmdlet
{
[Parameter(Mandatory = true)]
[ValidateNotNullOrEmpty]
public int Low { get; set; }
[Parameter(Mandatory = true)]
[ValidateNotNullOrEmpty]
public int High { get; set; }
protected override void BeginProcessing()
{
if (Low >= High)
{
// Is there a better exception to throw here?
throw new CmdletInvocationException("Low must be less than High");
}
base.BeginProcessing();
}
protected override void OnProcessRecord()
{
// Do stuff...
}
}
Is there is a better way to do this? The main thing I don't like about the solution above is that I can't throw a ParameterBindingException like the validation attributes would do since it's an internal class. I could throw ArgumentException or PSArgumentException but those are really for Methods not cmdlets.
You need something like in the cmdlet get-random.
Because you can't use [validatescript()] attribute in a cmdlet 'cause it's valid only for powershell function/script at run-time you need to steal the idea from microsoft.powershell.utility\get-random:
The value check is done in the BeginProcessing() and use a customized error ThrowMinGreaterThanOrEqualMax
protected override void BeginProcessing()
{
using (GetRandomCommand.tracer.TraceMethod())
{
if (this.SetSeed.HasValue)
this.Generator = new Random(this.SetSeed.Value);
if (this.EffectiveParameterSet == GetRandomCommand.MyParameterSet.RandomNumber)
{
if (this.IsInt(this.Maximum) && this.IsInt(this.Minimum))
{
int minValue = this.ConvertToInt(this.Minimum, 0);
int maxValue = this.ConvertToInt(this.Maximum, int.MaxValue);
if (minValue >= maxValue)
this.ThrowMinGreaterThanOrEqualMax((object) minValue, (object) maxValue);
this.WriteObject((object) this.Generator.Next(minValue, maxValue));
}
else
{
double min = this.ConvertToDouble(this.Minimum, 0.0);
double max = this.ConvertToDouble(this.Maximum, double.MaxValue);
if (min >= max)
this.ThrowMinGreaterThanOrEqualMax((object) min, (object) max);
this.WriteObject((object) this.GetRandomDouble(min, max));
}
}
else
{
if (this.EffectiveParameterSet != GetRandomCommand.MyParameterSet.RandomListItem)
return;
this.chosenListItems = new List<object>();
this.numberOfProcessedListItems = 0;
if (this.Count != 0)
return;
this.Count = 1;
}
}
}
...
private void ThrowMinGreaterThanOrEqualMax(object min, object max)
{
if (min == null)
throw GetRandomCommand.tracer.NewArgumentNullException("min");
if (max == null)
throw GetRandomCommand.tracer.NewArgumentNullException("max");
string errorId = "MinGreaterThanOrEqualMax";
this.ThrowTerminatingError(new ErrorRecord((Exception) new ArgumentException(this.GetErrorDetails(errorId, min, max).Message), errorId, ErrorCategory.InvalidArgument, (object) null));
}
You can use a decompiler ( dotPeak ) to see the rest of the code to learn more on custom error for cmdlet

A custom datetime struct

I am trying to develop a clock application with images for each digits from 0-9. Wrote a struct that gives me each digits every now and then. Following is the struct.
public struct TimeStruct
{
public DateTime dt
{
get
{
return DateTime.Now;
}
}
public int s
{
get
{
return dt.Second;
}
}
public int s2
{
get
{
return s % 10;
}
}
public int s1
{
get
{
return s / 10;
}
}
public int m
{
get
{
return dt.Minute;
}
}
public int m2
{
get
{
return m % 10;
}
}
public int m1
{
get
{
return m / 10;
}
}
public int h
{
get
{
return dt.Hour;
}
}
public int h2
{
get
{
return h % 10;
}
}
public int h1
{
get
{
return h / 10;
}
}
public int d
{
get
{
return (int)dt.DayOfWeek;
}
}
}
Please guide me to modify this struct so that the prop s2 should be set only when s1 becomes 0. And the same with minutes.
Technology Used : Silverlight
Platform : Windows Phone 7
Was that a bad idea to use struct?
What do you mean by "prop s2 should be set only when s1 becomes 0" - what do you want it to do when s1 isn't 0? Are you perhaps looking for nullable value types, where s1 would return the null value in some cases?
I have to say, I think this is a pretty confusing type. It has no real state - it's effectively just a bunch of static properties. Any reason for not implementing it as a bunch of static properties, e.g. in a CurrentDateTime class? Or just use DateTime.Now? Note that if you ask your struct for a bunch of values, one at a time, it may very well give you inconsistent results as time will pass. For example, suppose the time is 1:59:59 and you call s, then m, then h - you may end up getting 59, 59, 2 as the current time rolls over from 1:59:59 to 2:00:00 between the last two calls. If you take the value of DateTime.Now just once and ask it for all its properties, you'll get a consistent view.
Why re-invent the wheel ? Use DateTime and TimeSpan.

Categories