A way of writing large numeral literals in C# - c#

I have a method like this:
Prefix GetPrefix(decimal value)
{
if(value > 11000000000000000000)
return Prefix.CosmicBig;
if(value > 1000000000000000)
return Prefix.ReallyBig;
if(value > 3000000000000)
return Prefix.Big;
if(value > 50000000)
return Prefix.Okay;
if(value > 750000)
return Prefix.MostlyNormal;
if(value > 750000)
return Prefix.SoSo;
if(value > 750)
return Prefix.Small;
return Prefix.MiserablySmall;
}
The exact values are not important. What matters is that they are sometimes changed (the prefixes are used for drawing and some text areas change sizes in development). I'm looking for a way of writing these literals in a way that's easily readably by a human changing it, without having to count all the zeroes. A separator would be nice. I thought about writing 11 * 1000 * 1000 * 1000 * 1000 * 1000 * 1000, but that's only barely more manageable. Using Math.Pow() does it a little better, but I'm not comfortable with using such calculations to define constants.

Instead of 11000000000000000000 you can use 11e18. Use m to indicate that it is a decimal, so 11e18m.

You can introduce extension methods for int:
750.Thousand();
5.Million();
100.Billion();
The implementation of these methods is simple:
public static int Thousand(this int value)
{
return value * 1000;
}
public static int Million(this int value)
{
return value.Thousand() * 1000;
}
// etc.
Make sure to return the appropriate data type for methods for bigger numbers:
public static long Billion(this int value)
{
return value.Million() * 1000;
}
Unfortunatelly, you will have to write those methods for every integral or floating point type you want to support.
Having a full set of such extension methods would allow you to express your large numbers in relativly natural ways, even when it's not just all zeros at the end:
100.Billion() + 30.Thousand() + 300 // == 100,000,030,300
If you want to get fancy, you could even think about nesting them:
100.Billion(30.Thousand(300))
But I think that would lose some expressiveness, because people would wonder what the parameter means.
Still, implementation would look like this:
public static long Billion(this int value, long add)
{
return value.Million() * 1000 + add;
}
Using these extension methods has one little downside: Your numbers are no longer compile-time constants. They are calculated at runtime. In the vast majority of cases, this shouldn't be a problem.

A language feature introduced on C# 7.0 is the introduction of _ as a digit separator when inside number literals, so instead of 50000000 you can write 50_000_000.
https://devblogs.microsoft.com/dotnet/new-features-in-c-7-0/#literal-improvements
For very large numbers, or if working with numbers in scientific notation, the solution given by Sjoerd might be a better option.

Related

Can IEnumerable.Count() return a negative number? [duplicate]

This question already has answers here:
Why does .NET use int instead of uint in certain classes?
(7 answers)
Closed 4 years ago.
The C# IEnumerable.Count(IEnumerable<TSource>) and IEnumerable.Count(IEnumerable<TSource>, Func<TSource,Boolean>) both return type int implying it can return a value less than zero. It doesn't make sense for it to do so, but if the type is int it's theoretically possible for the result to be a negative value.
IEnumerable<string> list = GetMyList();
int listCount = list.Count();
// is it more correct to do this:
if(listCount <= 0)
{
DoSomething();
}
else
{
DoSomethingElse();
}
// or this:
if(listCount == 0)
{
DoSomething();
}
else if (listCount > 0)
{
DoSomethingElse();
}
else
{
// but this branch will never be hit
throw new Exception();
}
I can't find any information online about whether or not that can actually happen, and the Documentation for Enumerable.Count does not specify any cases in which it might.
Just wondering if anyone has any experience with this happening or any information on this.
Thanks
The purpose of the return data type is not to imply a range of numbers. Although it naturally does set a hard upper and lower limit (sort of... see LongCount), that is just a side effect of type compatibility and generalizability.
Consider the Array's Rank property. The maximum value is 32. But we don't store it in a byte or short. We store it in an int. Why? We don't need all that range. But it's recommended: they're fast (they align well to register size and memory maps) and, by convention, It is easier to work with other libraries if you work with ints. Also, the int datatype is CLS-compliant (meaning that any language that implements the CLR must support it) but uint32 is not.
Returning a numeric data type that has a particular range in no way implies that the full range will be used. And returning a negative value from IEnumerable.Count() would not only be poor form, but it would be semantically incorrect, as a count must obviously return a cardinal number, which must be and integer and non-negative.
Actually there´s no such method Count defined on neither IEnumerable nor IEnumerable<T>, but on the static class System.Linq.Enumerable. As it´s a (static) extension-method, you can´t override nor modify this at all. So let´s look into the extension-method:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator()) {
checked {
while (e.MoveNext()) count++;
}
}
return count;
}
As you can see the only way Count will ever return a negative number is by implelementing ICollection.Count, which is called by Enumerable.Count() (as you can see above), or by creating your own extension-method with the exact same name and relying on extension-method resolution in order to "hide" the method from System.Linq.Enumerable:
public static class MyClass
{
public static int Count(this IEnumerable<T> src) { return -1; }
}
However I can´t see any reason why one should do this at all.
So in short the method can return a negative number. Doing this however breaks the principle of least astonishment and thus is a bad idea.

How to find product of arithmetic progression?

I need to write a function to find a product of arithmetic progression elements (using recursion). I have only vague idea how to do it – something like this:
public static int product(int n)
{
if (n == 0)
return 0;
else
return <some code> * product(n-1);
}
Could you at least give me a hint?
The following code should do the trick:
public static int Product(int arithInitial, int arithDifference, int n)
{
if (n == 1)
return GetArithmeticSeriesTerm(arithInitial,arithDifference,1);
else
return GetArithmeticSeriesTerm(arithInitial,arithDifference,n) * Product(arithInitial, arithDifference, n-1);
}
public static int GetArithmeticSeriesTerm(int initial, int difference, int position)
{
return initial+difference*(position-1);
}
I have created a new method to get the elements of the arithmetic progression. I've also changed the base case of the recursion to be n==1 and then put the call to the arithmetic series term.
It should hopefully be pretty self explanatory as to what it does.
For the first four terms of the series 1,3,5,7,... you would call it as
int result = Product(1,2,4)`
Note: You don't need two methods for this but I feel that introducing the second method makes it clearer what the code is doing. You could of course just inline the expression and of course your base case can in fact be simplified to just initial if you wanted to make it a bit cleaner. Using the full method though makes it very intuitive of why we are doing that.
you need to write a function which take 3 arguments first Term(f) , common difference(d) , and total number of term (n) in AP.
int fun(int f,int d,int n){
if(n==0) return 1;
else (f+(n-1)*d) * fun(f,d,n--);
}

Converting a method to use any Enum

My Problem:
I want to convert my randomBloodType() method to a static method that can take any enum type. I want my method to take any type of enum whether it be BloodType, DaysOfTheWeek, etc. and perform the operations shown below.
Some Background on what the method does:
The method currently chooses a random element from the BloodType enum based on the values assigned to each element. An element with a higher value has a higher probability to be picked.
Code:
public enum BloodType
{
// BloodType = Probability
ONeg = 4,
OPos = 36,
ANeg = 3,
APos = 28,
BNeg = 1,
BPos = 20,
ABNeg = 1,
ABPos = 5
};
public BloodType randomBloodType()
{
// Get the values of the BloodType enum and store it in a array
BloodType[] bloodTypeValues = (BloodType[])Enum.GetValues(typeof(BloodType));
List<BloodType> bloodTypeList = new List<BloodType>();
// Create a list where each element occurs the approximate number of
// times defined as its value(probability)
foreach (BloodType val in bloodTypeValues)
{
for(int i = 0; i < (int)val; i++)
{
bloodTypeList.Add(val);
}
}
// Sum the values
int sum = 0;
foreach (BloodType val in bloodTypeValues)
{
sum += (int)val;
}
//Get Random value
Random rand = new Random();
int randomValue = rand.Next(sum);
return bloodTypeList[randomValue];
}
What I have tried so far:
I have tried to use generics. They worked out for the most part, but I was unable to cast my enum elements to int values. I included a example of a section of code that was giving me problems below.
foreach (T val in bloodTypeValues)
{
sum += (int)val; // This line is the problem.
}
I have also tried using Enum e as a method parameter. I was unable to declare the type of my array of enum elements using this method.
(Note: My apologies in advance for the lengthy answer. My actual proposed solution is not all that long, but there are a number of problems with the proposed solutions so far and I want to try to address those thoroughly, to provide context for my own proposed solution).
In my opinion, while you have in fact accepted one answer and might be tempted to use either one, neither of the answers provided so far are correct or useful.
Commenter Ben Voigt has already pointed out two major flaws with your specifications as stated, both related to the fact that you are encoding the enum value's weight in the value itself:
You are tying the enum's underlying type to the code that then must interpret that type.
Two enum values that have the same weight are indistinguishable from each other.
Both of these issues can be addressed. Indeed, while the answer you accepted (why?) fails to address the first issue, the one provided by Dweeberly does address this through the use of Convert.ToInt32() (which can convert from long to int just fine, as long as the values are small enough).
But the second issue is much harder to address. The answer from Asad attempts to address this by starting with the enum names and parsing them to their values. And this does indeed result in the final array being indexed containing the corresponding entries for each name separately. But the code actually using the enum has no way to distinguish the two; it's really as if those two names are a single enum value, and that single enum value's probability weight is the sum of the value used for the two different names.
I.e. in your example, while the enum entries for e.g. BNeg and ABNeg will be selected separately, the code that receives these randomly selected value has no way to know whether it was BNeg or ABNeg that was selected. As far as it knows, those are just two different names for the same value.
Now, even this problem can be addressed (but not in the way that Asad attempts to…his answer is still broken). If you were, for example, to encode the probabilities in the value while still ensuring unique values for each name, you could decode those probabilities while doing the random selection and that would work. For example:
enum BloodType
{
// BloodType = Probability
ONeg = 4 * 100 + 0,
OPos = 36 * 100 + 1,
ANeg = 3 * 100 + 2,
APos = 28 * 100 + 3,
BNeg = 1 * 100 + 4,
BPos = 20 * 100 + 5,
ABNeg = 1 * 100 + 6,
ABPos = 5 * 100 + 7,
};
Having declared your enum values that way, then you can in your selection code divide the enum value by 100 to obtain its probability weight, which then can be used as seen in the various examples. At the same time, each enum name has a unique value.
But even solving that problem, you are still left with problems related to the choice of encoding and representation of the probabilities. For example, in the above you cannot have an enum that has more than 100 values, nor one with weights larger than (2^31 - 1) / 100; if you want an enum that has more than 100 values, you need a larger multiplier but that would limit your weight values even more.
In many scenarios (maybe all the ones you care about) this won't be an issue. The numbers are small enough that they all fit. But that seems like a serious limitation in what seems like a situation where you want a solution that is as general as possible.
And that's not all. Even if the encoding stays within reasonable limits, you have another significant limit to deal with: the random selection process requires an array large enough to contain for each enum value as many instances of that value as its weight. Again, if the values are small maybe this is not a big problem. But it does severely limit the ability of your implementation to generalize.
So, what to do?
I understand the temptation to try to keep each enum type self-contained; there are some obvious advantages to doing so. But there are also some serious disadvantages that result from that, and if you truly ever try to use this in a generalized way, the changes to the solutions proposed so far will tie your code together in ways that IMHO negate most if not all of the advantage of keeping the enum types self-contained (primarily: if you find you need to modify the implementation to accommodate some new enum type, you will have to go back and edit all of the other enum types you're using…i.e. while each type looks self-contained, in reality they are all tightly coupled with each other).
In my opinion, a much better approach would be to abandon the idea that the enum type itself will encode the probability weights. Just accept that this will be declared separately somehow.
Also, IMHO is would be better to avoid the memory-intensive approach proposed in your original question and mirrored in the other two answers. Yes, this is fine for the small values you're dealing with here. But it's an unnecessary limitation, making only one small part of the logic simpler while complicating and restricting it in other ways.
I propose the following solution, in which the enum values can be whatever you want, the enum's underlying type can be whatever you want, and the algorithm uses memory proportionally only to the number of unique enum values, rather than in proportion to the sum of all of the probability weights.
In this solution, I also address possible performance concerns, by caching the invariant data structures used to select the random values. This may or may not be useful in your case, depending on how frequently you will be generating these random values. But IMHO it is a good idea regardless; the up-front cost of generating these data structures is so high that if the values are selected with any regularity at all, it will begin to dominate the run-time cost of your code. Even if it works fine today, why take the risk? (Again, especially given that you seem to want a generalized solution).
Here is the basic solution:
static T NextRandomEnumValue<T>()
{
KeyValuePair<T, int>[] aggregatedWeights = GetWeightsForEnum<T>();
int weightedValue =
_random.Next(aggregatedWeights[aggregatedWeights.Length - 1].Value),
index = Array.BinarySearch(aggregatedWeights,
new KeyValuePair<T, int>(default(T), weightedValue),
KvpValueComparer<T, int>.Instance);
return aggregatedWeights[index < 0 ? ~index : index + 1].Key;
}
static KeyValuePair<T, int>[] GetWeightsForEnum<T>()
{
object temp;
if (_typeToAggregatedWeights.TryGetValue(typeof(T), out temp))
{
return (KeyValuePair<T, int>[])temp;
}
if (!_typeToWeightMap.TryGetValue(typeof(T), out temp))
{
throw new ArgumentException("Unsupported enum type");
}
KeyValuePair<T, int>[] weightMap = (KeyValuePair<T, int>[])temp;
KeyValuePair<T, int>[] aggregatedWeights =
new KeyValuePair<T, int>[weightMap.Length];
int sum = 0;
for (int i = 0; i < weightMap.Length; i++)
{
sum += weightMap[i].Value;
aggregatedWeights[i] = new KeyValuePair<T,int>(weightMap[i].Key, sum);
}
_typeToAggregatedWeights[typeof(T)] = aggregatedWeights;
return aggregatedWeights;
}
readonly static Random _random = new Random();
// Helper method to reduce verbosity in the enum-to-weight array declarations
static KeyValuePair<T1, T2> CreateKvp<T1, T2>(T1 t1, T2 t2)
{
return new KeyValuePair<T1, T2>(t1, t2);
}
readonly static KeyValuePair<BloodType, int>[] _bloodTypeToWeight =
{
CreateKvp(BloodType.ONeg, 4),
CreateKvp(BloodType.OPos, 36),
CreateKvp(BloodType.ANeg, 3),
CreateKvp(BloodType.APos, 28),
CreateKvp(BloodType.BNeg, 1),
CreateKvp(BloodType.BPos, 20),
CreateKvp(BloodType.ABNeg, 1),
CreateKvp(BloodType.ABPos, 5),
};
readonly static Dictionary<Type, object> _typeToWeightMap =
new Dictionary<Type, object>()
{
{ typeof(BloodType), _bloodTypeToWeight },
};
readonly static Dictionary<Type, object> _typeToAggregatedWeights =
new Dictionary<Type, object>();
Note that the work of actually selecting a random value is simply a matter of choosing a non-negative random integer less than the sum of the weights, and then using a binary search to find the appropriate enum value.
Once per enum type, the code will build the table of values and weight-sums that will be used for the binary search. This result is stored in a cache dictionary, _typeToAggregatedWeights.
There are also the objects that have to be declared and which will be used at run-time to build this table. Note that the _typeToWeightMap is just in support of making this method 100% generic. If you wanted to write a different named method for each specific type you wanted to support, that could still used a single generic method to implement the initialization and selection, but the named method would know the correct object (e.g. _bloodTypeToWeight) to use for initialization.
Alternatively, another way to avoid the _typeToWeightMap while still keeping the method 100% generic would be to have the _typeToAggregatedWeights be of type Dictionary<Type, Lazy<object>>, and have the values of the dictionary (the Lazy<object> objects) explicitly reference the appropriate weight array for the type.
In other words, there are lots of variations on this theme that would work fine. But they will all have essentially the same structure as above; semantics would be the same and performance differences would be negligible.
One thing you'll notice is that the binary search requires a custom IComparer<T> implementation. That is here:
class KvpValueComparer<TKey, TValue> :
IComparer<KeyValuePair<TKey, TValue>> where TValue : IComparable<TValue>
{
public readonly static KvpValueComparer<TKey, TValue> Instance =
new KvpValueComparer<TKey, TValue>();
private KvpValueComparer() { }
public int Compare(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y)
{
return x.Value.CompareTo(y.Value);
}
}
This allows the Array.BinarySearch() method to correct compare the array elements, allowing a single array to contain both the enum values and their aggregated weights, but limiting the binary search comparison to just the weights.
Assuming your enum values are all of type int (you can adjust this accordingly if they're long, short, or whatever):
static TEnum RandomEnumValue<TEnum>(Random rng)
{
var vals = Enum
.GetNames(typeof(TEnum))
.Aggregate(Enumerable.Empty<TEnum>(), (agg, curr) =>
{
var value = Enum.Parse(typeof (TEnum), curr);
return agg.Concat(Enumerable.Repeat((TEnum)value,(int)value)); // For int enums
})
.ToArray();
return vals[rng.Next(vals.Length)];
}
Here's how you would use it:
var rng = new Random();
var randomBloodType = RandomEnumValue<BloodType>(rng);
People seem to have their knickers in a knot about multiple indistinguishable enum values in the input enum (for which I still think the above code provides expected behavior). Note that there is no answer here, not even Peter Duniho's, that will allow you to distinguish enum entries when they have the same value, so I'm not sure why this is being considered as a metric for any potential solutions.
Nevertheless, an alternative approach that doesn't use the enum values as probabilities is to use an attribute to specify the probability:
public enum BloodType
{
[P=4]
ONeg,
[P=36]
OPos,
[P=3]
ANeg,
[P=28]
APos,
[P=1]
BNeg,
[P=20]
BPos,
[P=1]
ABNeg,
[P=5]
ABPos
}
Here is what the attribute used above looks like:
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class PAttribute : Attribute
{
public int Weight { get; private set; }
public PAttribute(int weight)
{
Weight = weight;
}
}
and finally, this is what the method to get a random enum value would like:
static TEnum RandomEnumValue<TEnum>(Random rng)
{
var vals = Enum
.GetNames(typeof(TEnum))
.Aggregate(Enumerable.Empty<TEnum>(), (agg, curr) =>
{
var value = Enum.Parse(typeof(TEnum), curr);
FieldInfo fi = typeof (TEnum).GetField(curr);
var weight = ((PAttribute)fi.GetCustomAttribute(typeof(PAttribute), false)).Weight;
return agg.Concat(Enumerable.Repeat((TEnum)value, weight)); // For int enums
})
.ToArray();
return vals[rng.Next(vals.Length)];
}
(Note: if this code is performance critical, you might need to tweak this and add caching for the reflection data).
Some of this you can do and some of it isn't so easy. I believe the following extension method will do what you describe.
static public class Util {
static Random rnd = new Random();
static public int PriorityPickEnum(this Enum e) {
// The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong
// However, Random only supports a int (or double) as a max value. Either way
// it doesn't have the range for uint, long and ulong.
//
// sum enum
int sum = 0;
foreach (var x in Enum.GetValues(e.GetType())) {
sum += Convert.ToInt32(x);
}
var i = rnd.Next(sum); // get a random value, it will form a ratio i / sum
// enums may not have a uniform (incremented) value range (think about flags)
// therefore we have to step through to get to the range we want,
// this is due to the requirement that return value have a probability
// proportional to it's value. Note enum values must be sorted for this to work.
foreach (var x in Enum.GetValues(e.GetType()).OfType<Enum>().OrderBy(a => a)) {
i -= Convert.ToInt32(x);
if (i <= 0) return Convert.ToInt32(x);
}
throw new Exception("This doesn't seem right");
}
}
Here is an example of using this extension:
BloodType bt = BloodType.ABNeg;
for (int i = 0; i < 100; i++) {
var v = (BloodType) bt.PriorityPickEnum();
Console.WriteLine("{0}: {1}({2})", i, v, (int) v);
}
This should work pretty well for enum's of type byte, sbyte, ushort, short and int. Once you get beyond int (uint, long, ulong) the problem is the Random class. You can adjust the code to use doubles generated by Random, which would cover uint, but the Random class just doesn't have the range to cover long and ulong. Of course you could use/find/write a different Random class if this is important.

Code Contracts: How to express these conditions?

I'm playing around with Code Contracts at the moment and I'm not completely sure whether the static methods of the Contract class are powerful enough to compete with mathematical notation of conditions.
Let's assume we got a simple factorial method
int Factorial(int n);
I would express the following conditions:
Precondition:
n >= 0
Postconditions:
Factorial(n) = 1, in case n = 0
Factorial(n) = n*(n-1)*...*1, in case n > 0
These conditions clearly specify the behavior of Factorial in a short and clean way. My question is, whether they can be expressed through Code Contracts.
The precondition is trivial:
Contract.Requires(n >= 0)
The conditional post condition might be expresses using
if(n==0)
Contract.Ensures(Contract.Result<int>() == 1);
if(n > 0)
...
But I don't like the way I need the "if" statement here as it makes the plain list of pre- and postconditions harder to read. I hoped we would have something like
Contract.Ensures(...).InCase(...);
And last but not least, I do not have any idea how to express this, which is a common notation regarding math:
n*(n-1)*...*1
Guess I would need some kind of loop, but this would copy the whole implementation. Is there any smart way to express such notations?
Thank you in advance.
What you are looking for are Unit Tests, not Code Contracts.
Tipically, checks like if n=0, then f(n) = 1 and if n=3, then f(n) = 6 are Test Cases that should be expressed as Unit Tests.
In your case, I think a suitable post condition would be something like "The result is always >= 1". And nothing more than that.
Assuming that your factorial class looks something like this:
public class Factorial
{
public int Compute(int n)
{
if (n == 0)
return 1;
return n * Compute(n - 1);
}
}
a suitable Unit Test written with the NUnit Framework would be:
[TestFixture]
public class FactorialTests
{
[TestCase(0, 1)]
[TestCase(1, 1)]
[TestCase(2, 2)]
[TestCase(7, 5040)]
[TestCase(10, 3628800)]
public void Compute_ReturnsCorrectResult(int n, int expectedResult)
{
var sut = new Factorial();
Assert.AreEqual(expectedResult, sut.Compute(n));
}
}
Update (after the comments)
Stating result >= 1 does not fully specify the algorithm.
I don't think the Code Contract's job is to specify the algorithm in detail. the algorithm is specified by the method.
If the Code Contract was a complex piece of logic like the method itself, then I guess we would need a Code Contract Contract to verify that the Code Contract performs the correct checks. This obviously leads to infinite recursion.
I didn't expect n*(n-1)*...*1 to be accepted by the compiler. But some generic range operator in a LINQ-flavoured way would surely be a gread addition, e.g. From(n).To(1).Product() or From(n).To(m).Sum()
If there was such a form of expressing factorials (and probably there is) you could certainly use it in your code, rather than the Code Contracts.
Update 2
Just for fun, I found a LINQ way of computing Factorials:
Enumerable.Range(1, n == 0 ? 1 : n).Aggregate((a, i) => a * i);
You could try to the following:
Contract.Ensures(Contract.Result<int>() == AlternativeFactorial(n));
where AlternativeFactorial is:
[Pure]
public static int AlternativeFactorial(int n)
{
if(n==0)
return 1;
if(n > 0)
{
//Alternative implementation.
}
}
Of course anything you use in a contract should be side-effect free (pure).
Now as far as the factorial implementation, I cannot come up with a more compact "alternative" implementation than w0lf's. What you should consider though is changing the return value of your method from int to BigInteger. Factorials can get very large very quickly. Also note that by adding a post-condition on the factorial value, you will pretty much double the time your method will take to return a result. This can be resolved by building CodeContracts only on the debug configuration.

Is there a class in C# to handle a couple of INT (range of 2 INT- 1-10)

I am quite new to C# and I was wondering if there is a Class or a data structure or the best way to handle the following requirement...
I need to handle a COUPLE of int that represent a range of data (eg. 1 - 10 or 5-245) and I need a method to verify if an Int value is contained in the range...
I believe that in C# there is a class built in the framework to handle my requirement...
what I need to do is to verify if an INT (eg. 5) is contained in the range of values Eg (1-10) ...
in the case that I should discover that there is not a class to handle it, I was thinking to go with a Struct that contain the 2 numbers and make my own Contain method to test if 5 is contained in the range 1-10)
in the case that I should discover that there is not a class to handle
it, I was thinking to go with a Struct that contain the 2 numbers and
make my own Contain method to test if 5 is contained in the range
1-10)
That's actually a great idea as there's no built-in class for your scenario in the BCL.
You're looking for a range type; the .Net framework does not include one.
You should make an immutable (!) Int32Range struct, as you suggested.
You may want to implement IEnumerable<int> to allow users to easily loop through the numbers in the range.
You need to decide whether each bound should be inclusive or exclusive.
[Start, End) is probably the most obvious choice.
Whatever you choose, you should document it clearly in the XML comments.
Nothing exists that meets your requirements exactly.
Assuming I understood you correctly, the class is pretty simple to write.
class Range
{
public int Low {get; set;}
public int High {get; set;}
public bool InRange(int val) { return val >= Low && val <= High; }
}
A Tuple<int,int> would get you part of the way but you'd have to add an extension method to get the extra behavior. The downside is that the lower- and upper-bounds are implicitly Item1 and Item2 which could be confusing.
// written off-the-cuff, may not compile
public static class TupleExtension
{
public static bool InRange(Tuple<int, int> this, int queryFor)
{
return this.Item1 >= queryFor && this.Item2 <= queryFor;
}
}
You could create an extension if you want to avoid making a new type:
public static class Extensions
{
public static bool IsInRange(this int value, int min, int max)
{
return value >= min && value <= max;
}
}
Then you could do something like:
if(!value.IsInRange(5, 545))
throw new Exception("Value is out of range.");
i think you can do that with an array.
some nice examples and explanation can be found here:
http://www.dotnetperls.com/int-array
Nothing built in AFAIK, but (depending on the size of the range) an Enumerable.Range would work (but be less than optimal, as you're really storing every value in the range, not just the endpoints). It does allow you to use the LINQ methods (including Enumerable.Contains), though - which may come in handy.
const int START = 5;
const int END = 245;
var r = Enumerable.Range(START, (END - START)); // 2nd param is # of integers
return r.Contains(100);
Personally, I'd probably go ahead and write the class, since it's fairly simple (and you can always expose an IEnumerable<int> iterator via Enumerable.Range if you want to do LINQ over it)

Categories

Resources