Get a variable value from a method to another? - c#

In this situation, I am trying to get the value of "FileSizeType", an integer variable, into the method that's under it "NomCategorie"and convert it to a string(that's what the comment says).
static int ChoisirCategory()
{
int FileSizeType;
Console.Write("What type do you want: ");
FileSizeType = Convert.ToInt32(Console.ReadLine());
return FileSizeType;
}
static string NomCategorie(int c)
{
//get FileSizeType into c
string FileType;
if (c == 1)
{
return FileType = "Small";
}
else if (c == 2)
{
return FileType = "Medium";
}
else if (c == 3)
{
return FileType = "Large";
}
else if (c == 4)
{
return FileType = "Huge";
}
else
{
return FileType="Non-valid choice";
}

I would suggest using enum class
public enum Test
{
Small = 1,
Medium = 2,
Large = 3,
Huge = 4
}
then you can simply convert the number by using
int integer = 1;
if (Enum.IsDefined(typeof(Test), integer)
{
Console.WriteLine((Test)integer).
}
else
{
Console.WriteLine("Bad Integer");
}
output:
Small

Looking at your existing code, you are already returning the value of FileSizeType from the ChoisirCategory, so you can capture it in a variable and then pass that to the NomCategorie method to get the category name, for example:
int categoryId = ChoisirCategory();
string categoryName = NomCategorie(categoryId);
Note that there are many other improvements that can be made (for example, what happens if the user types in "two" instead of "2"?), but I think those suggestions may be out of scope based on the question.

Here's how your code could be simplified if you combine both suggestions above. I've also added an if clause to verify that the value is not higher than those available.
static enum AvailableSizes
{
Small = 1,
Medium = 2,
Large = 3,
Huge = 4
}
static int ChoisirCategory()
{
int FileSizeType;
GetInput:
Console.Write("What type do you want: ");
FileSizeType = Convert.ToInt32(Console.ReadLine());
// Ensure the value is not higher than expected
// (you could also check that it is not below the minimum value)
if (FileSizeType > Enum.GetValues(typeof(AvailableSizes)).Cast<int>().Max());
{
Console.WriteLine("Value too high.");
goto GetInput;
}
return FileSizeType;
}
static string NomCategorie(int c)
{
if (Enum.IsDefined(typeof(AvailableSizes), c)
{
return (AvailableSizes)c;
}
else
{
return "Invalid category";
}
}
Then somewhere in your code you would call these using this statement
string categoryStr = NomCategorie(ChoisirCategory());
Console.WriteLinte(categoryStr); // or do whatever you want with the returned value
With this code, if the input is higher than 4, it will output "Value too high." and ask the question again until the value is no higher than 4.
If the user types 0 or a negative value, then it will output "Invalid category".
This might help you decide where you want to handle input errors: right after user input or during the parsing of the number to a string.

Related

How to dynamically validate data types in C#?

So I have a program for this class where I'm supposed to allow the user to select a data type like byte, int, short, long, and etc. And then I am supposed to validate and make sure that the numbers they enter for the math problem aren't outside of the bounds of the data type they selected. Right now I'm using a bunch of if statements for each individual data type, and checking if it's above MaxValue or below MinValue. However, there has to be a better way to do this, right?
My current code is like this (numType is a byte set to the value of a constant when the button is pressed):
if(numType = BYTE){
if(leftNum > byte.MaxValue){
errorsEncountered = true;
returnString = returnString + "Left number must not be more than " +
byte.MaxValue.ToString() + " for a byte.\n";
}
if(leftNum < byte.MinValue){
errorsEncountered = true;
returnString = returnString + "Left number must not be less than " +
byte.MinValue.ToString() + " for a byte.\n";
... (so on and so forth)
}
However I'd like to think that you could instead use something like a variable to record the data type and use that instead. So lets say that you have an array of each potential value of numType (1-7 in this case). Would there be a way to do something like this?
byte numType = 8; // Will never be returned as this, an error is output for this value immediately.
const byte BYTE = 0;
const byte SHORT = 1;
const byte INT = 2;
const byte LONG = 3;
const byte FLOAT = 4;
const byte DOUBLE = 5;
const byte DECIMAL = 6;
string[] dataTypes = {"byte", "short", "int", "long", "float", "double", "decimal"};
if(leftNum > dataTypes[numType].MaxValue) {
errorsEncountered = true;
returnString = "Left number must not be more than " +
dataTypes[numType].MaxValue.ToString() + " for a " + dataTypes[numType] + ".";
}
if(leftNum < dataTypes[numType].MinValue) {
errorsEncountered = true;
returnString = "Left number must not be more than " +
dataTypes[numType].MinValue.ToString() + " for a " + dataTypes[numType] + ".";
}
I know my demonstration is incredibly simplistic but I genuinely don't know how better to describe what I'm trying to do. Thank you for any help you can provide.
Edit: Honestly it seems I'm a bit out of my depth here. I have no clue what most of these solutions are actually doing, and I've come out of this with the impression that I should probably just work on learning the language as a whole.
You can keep a dictionary of all the types you want to use with the string you want the user to type in to refer to that type, then use reflection to call TryParse and MinValue/MaxValue if needed.
Something like this:
public static readonly Dictionary<string, Type> aliases = new() {
{ "byte", typeof(byte) },
{ "short" , typeof(short) },
{ "int" , typeof(int) },
{ "long" , typeof(long) },
{ "float" , typeof(float) },
{ "double" , typeof(double) },
{ "decimal" , typeof(decimal) }
};
static void Main() {
Type type;
while (true) {
Console.WriteLine("Enter the type:");
var selectedType = Console.ReadLine().Trim();
if (!aliases.TryGetValue(selectedType, out type)) {
Console.WriteLine("You did it wrong");
continue;
}
break;
}
while (true) {
Console.WriteLine("Type a value:");
var value = Console.ReadLine().Trim();
// Create an instance of whatever type we're using
object result = Activator.CreateInstance(type);
// Get a reference to the TryParse method for this type
var tryParseMethod = type.GetMethod("TryParse", new[] { typeof(string), type.MakeByRefType() });
// Call TryParse
if (tryParseMethod.Invoke(null, new[] { value, result }) is bool success && success) {
Console.WriteLine("You did it right!");
break;
} else {
// TryParse failed, so show the user the min/max values
var minValueProp = type.GetField("MinValue");
var maxValueProp = type.GetField("MaxValue");
Console.WriteLine($"You did it wrong. Enter a value between {minValueProp.GetValue(result)} and {maxValueProp.GetValue(result)}");
continue;
}
}
}
For example:
Enter the type:
byte
Type a value:
-1
You did it wrong. Enter a value between 0 and 255
Type a value:
1
You did it right!
A bit more of code would be helpful, for instance how leftNum and numType are declared.
Let's try to help anyway. You can try a polymorphic approach.
It may be overkill, but it's a nice exercise:
Create an interface, let's say ITypeValidator
Create the implementations of such Interface, i.e. IntegerValidator : ITypeValidator
Have a Dictionary containing your validators and their respective type Dictionary<byte, ITypeValidator>
Initialize your types in your Dictionary.
Sample:
internal class Program
{
static void Main(string[] args)
{
var myTypes = GetTypesImplementingInterface();
byte numType = 2;
object leftNum = 3;
var validator = myTypes[numType];
var result = validator.Validate(leftNum);
}
public static Dictionary<byte, ITypeValidator> GetTypesImplementingInterface()
{
// Fancy approach would be to use reflection to scan the Assembly
// for implementations of our interface and automatically
// instantiate and fill in the Dictionary
return new Dictionary<byte, ITypeValidator>()
{
{ 2, new IntValidator() }
};
}
public interface ITypeValidator
{
byte TypeNumber { get; } // Tip: this helps with the Reflection approach
ValidationResult Validate(object number);
}
public class ValidationResult
{
public bool HasError { get; set; }
public List<string> ErrorMessages { get; set; }
}
public class IntValidator : ITypeValidator
{
public byte TypeNumber => 2;
public ValidationResult Validate(object number)
{
// do your things
return new ValidationResult();
}
}
}
If your validations are always the same (min and max), you can also think of adding a base class, with abstract Minimum and Maximum properties and a base validation method. So for each type, you just need to implement the TypeNumber, Min and Max values.

Compare Enums in if statement

I want to compare some enums on ifs statements here is what I mean but this isnt working. Basically I want to see if the injury is the same as the enum so if for example the injury is bleeding you need to bandage etc. If you need any other information please let me know.
static string injuries = GetInjuriesName(GetInjuries(closestPlayer));
EPedInjuries result;
if (Enum.TryParse(injuries, out result) && result == EPedInjuries.Overdose)
{
.....
}
else if (Enum.TryParse(injuries, out result) && result == EPedInjuries.GunShotWound)
{
....
}
....
public enum EPedInjuries
{
OpenFracture,
GunShotWound,
Fever,
BrokenLeg,
BrokenArm,
BrokenRib,
Overdose,
.....
}
public static EPedInjuries GetInjuries(Ped ped)
{
Ped = ped;
int num = API.Common.Random.Next(0, 101);
if (num >= 0 && num <= 37)
{
return EPedInjuries.Overdose;
}
if (num > 37 && num <= 55)
{
return EPedInjuries.GunShotWound;
}
if (num > 55 && num <= 72)
{
return EPedInjuries.CardiacArrest;
}
....
}
public static string GetInjuriesName(EPedInjuries injuries)
{
string result = string.Empty;
switch (injuries)
{
case EPedInjuries.Overdose:
result = "~r~Overdose";
break;
case EPedInjuries.GunShotWound:
result = "~r~Gunshot Wound";
break;
case EPedInjuries.CardiacArrest:
result = "~r~Cardiac Arrest";
break;
....
}
return result;
}
The GetInjuries method already returns the enum type you want to compare. As #Johnny Mopp points out, you are getting the enum, converting it to some string, only to try to convert it back to an enum
Just do
EPedInjuries result = GetInjuries(closestPlayer);
if (result == EPedInjuries.Overdose)
{
.....
}
else if (result == EPedInjuries.GunShotWound)
{
....
}
Your GetInjuriesName returns string which is not correct enum value and Enum.TryParse can't parse it and returns false:
var injuries = GetInjuriesName(EPedInjuries.Overdose);
Console.WriteLine(injuries); //prints "~r~Overdose"
Console.WriteLine(Enum.TryParse(injuries, out EPedInjuries result)); // prints "False"
Just introduce two fields/variables one for name and one for injury enum value:
EPedInjuries injury = GetInjuries(closestPlayer)
string injuryName = GetInjuriesName(injury);
And use the first one to handle the logic:
if(injury == EPedInjuries.Overdose)
{
....
}
.....
I believe you will receive strings from somewhere else so you want to convert that's why you have that GetInjuriesName method. For me, you should just switch on the strings you receive.
For the sake of fixing this code, you should remove the "~r~" from the strings so that it matches any enum type.
Also, enums can't be null so you will always get the first value which is the Default.
public static string GetInjuriesName(EPedInjuries injuries)
{
string result = string.Empty;
switch (injuries)
{
case EPedInjuries.Overdose:
result = "Overdose";
break;
case EPedInjuries.GunShotWound:
result = "Gunshot Wound";
break;
case EPedInjuries.CardiacArrest:
result = "Cardiac Arrest";
break;
}
return result;
}
Then the TryParse will work and produce a value contained inside the enum.

Is there an easier way to parse an int to a generic Flags enum?

I've got a generic function to parse an object into a generic Enum.
However, I'm running into an issue when trying to safely parse an int into a [Flags] Enum.
Directly using Enum.ToObject() works to parse valid combinations, but will just return the original value if there isn't a flag combination.
Additionally, when there's no explicit enum member for a combination of flags, Enum.ToName() and Enum.IsDefined() don't return helpful values.
For Example:
[Flags]
public enum Color
{
None = 0,
Red = 1,
Green = 2,
Blue = 4,
}
// Returns 20
Enum.ToObject(typeof(Color), 20)
// Returns ""
Enum.ToName(typeof(Color), 3)
// Returns false
Enum.IsDefined(typeof(Color), 3)
I've written a function that I think technically works, but it seems like there has to be a better way to do this.
My Function:
public static T ParseEnumerator<T>(object parseVal, T defaultVal) where T : struct, IConvertible
{
Type ttype = typeof(T);
if (!ttype.IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
bool isFlag = ttype.GetCustomAttribute(typeof(FlagsAttribute)) != null;
try
{
if (parseVal == null)
{
return defaultVal;
}
else if (parseVal is T)
{
return (T)parseVal;
}
else if (parseVal is string)
{
return (T)Enum.Parse(ttype, parseVal.ToString(), true);
}
//**************** The section at issue **********************************/
else if (isFlag && parseVal is int)
{
List<string> flagsList = new List<string>();
int maxVal = 0;
//Loop through each bit flag
foreach (var val in Enum.GetValues(ttype))
{
if (CountBits((int)val) == 1)
{
if ((int)val > maxVal)
maxVal = (int)val;
// If the current bit is set, add the flag to the result
if (((int)parseVal & (int)val) == (int)val)
{
string enumName = Enum.GetName(ttype, val);
if (!string.IsNullOrEmpty(enumName))
flagsList.Add(enumName);
}
}
}
// Is the value being parsed over the highest bitwise value?
if ((int)parseVal >= (maxVal << 1))
return defaultVal;
else
return (T)Enum.Parse(ttype, string.Join(",", flagsList));
}
//************************************************************************/
else
{
string val = Enum.GetName(ttype, parseVal);
if (!string.IsNullOrEmpty(val))
return (T)Enum.ToObject(ttype, parseVal);
else
return defaultVal;
}
}
catch
{
return defaultVal;
}
}
Is there something I'm missing? Or is there another way to parse these values safely?
Any help is appreciated, thanks!
-MM
Since your generic function has to know the Enum type to begin with, you can just scrap the function and use basic casting instead.
using System;
namespace SO_58455415_enum_parsing {
[Flags]
public enum CheeseCharacteristics {
Yellow = 1,
Mouldy = 2,
Soft = 4,
Holed = 8
}
public static class Program {
static void Main(string[] args) {
CheeseCharacteristics cc = (CheeseCharacteristics)12;
Console.WriteLine(cc);
}
}
}
If all you want to know is if you have a value that can be created using the enum flags.. that's pretty easy, as long as we can assume that each flag is "sequential" (e.g. there are no gaps between the flags). All numbers between 1 and the sum of all flag values can be made by some combination of flags. You can simply sum the flag values together and compare that to your question value.
public static bool IsValidFlags<T>(int checkValue) where T:Enum {
int maxFlagValue = ((int[])Enum.GetValues(typeof(T))).Sum();
return (0 < checkValue) && (checkValue <= maxFlagValue);
}
For future reference, you can constrain your generic parameter to an enum:
void fx<T> () where T:Enum { }

How to validate decimal places

Please let me know good way to validate decimal value, if decimal(4,2) it should accept 2 numeric and 2 decimal places.
var value = "44.29";
var dec = value.Split('.');
Then finding the length will can be used, I need a better culture specific way. I need a generic solution which can be applied to all decimal field.
Like:
validate(int before,int afterdecimal);
var valid = validate(2,2);
Need a generic cleaner solution for this
private static bool IsDecimal(string value, int before, int after)
{
if (value.Contains("."))
{
var parts = value.Split('.');
if (parts[0].Length == before && parts[1].Length == after)
return true;
}
else if(value.Length == before)
return false;
return true;
}
You can try like this:
[RegularExpression(#"^\d{1,2}(\.\d{0,2})$",ErrorMessage = "Value contains more than 2 decimal places")]
public decimal Value { get; set; }
If you whant just validate, try to use the mod:
44.29 % 1 = 0.29
From the above answers I was able to do it like this
string value = "2009.99";
if (IsDecimal(value, 4, 4))
{
Console.WriteLine("Valid");
}
private static bool IsDecimal(string value, int before, int after)
{
var r = new Regex(#"^\d{1," + before + #"}(\.\d{0," + after + #"})$");
return r.IsMatch(value);
}

How to get the full value from an excel cell, not the displayed (rounded) value?

I'm having an issue retrieving the exact value from an cell from a worksheet. If I open the file the cell has a decimal number which it's shown only with 4 decimals, but if I click on the particular cell, the value is different, having 6 decimals. I know it's a setting applied to the cell in order to show only 4 decimals.
Now I'm trying to retrieve the cell's data in C#, using ClosedXML.Excel, not Microsoft.Office.Interop.Excel, but the only thing I'm able to get is the 4 decimals value, not the "whole" one, which is a great issue as I have calculation later on and I have big differences due to the missing 2 decimals.
I have tried using inputSheet.Worksheet.Cell(row,col).GetDouble(), or Convert.ToDouble(inputSheet.Worksheet.Cell(row, col).Value), or even to refer to the "RichText" property, or ...ToString("0.000000"), but no matter what I use, I can only retrieve the 4 decimal value instead of the full 6 decimals one.
I got a look at the source code of the library, here is what the Value getter does:
get
{
string str = this.FormulaA1;
if (!XLHelper.IsNullOrWhiteSpace(str))
{
string str2;
string sName;
if (str[0] == '{')
{
str = str.Substring(1, str.Length - 2);
}
if (str.Contains<char>('!'))
{
sName = str.Substring(0, str.IndexOf('!'));
if (sName[0] == '\'')
{
sName = sName.Substring(1, sName.Length - 2);
}
str2 = str.Substring(str.IndexOf('!') + 1);
}
else
{
sName = this.Worksheet.Name;
str2 = str;
}
if (this._worksheet.Workbook.WorksheetsInternal.Any<XLWorksheet>(w => (string.Compare(w.Name, sName, true) == 0)) && XLHelper.IsValidA1Address(str2))
{
return this._worksheet.Workbook.Worksheet(sName).Cell(str2).Value;
}
object obj2 = this.Worksheet.Evaluate(str);
IEnumerable enumerable = obj2 as IEnumerable;
if ((enumerable != null) && !(obj2 is string))
{
using (IEnumerator enumerator = enumerable.GetEnumerator())
{
while (enumerator.MoveNext())
{
return enumerator.Current;
}
}
}
return obj2;
}
string s = this.HasRichText ? this._richText.ToString() : this._cellValue;
if (this._dataType == XLCellValues.Boolean)
{
return (s != "0");
}
if (this._dataType == XLCellValues.DateTime)
{
return DateTime.FromOADate(double.Parse(s));
}
if (this._dataType == XLCellValues.Number)
{
return double.Parse(s);
}
if (this._dataType == XLCellValues.TimeSpan)
{
return TimeSpan.Parse(s);
}
return s;
}
I noticed, that when it reach string s = this.HasRichText ? this._richText.ToString() : this._cellValue;, if you have never called cell.RichText (so neither viewing it with the debugger) you get the correct value, because this.HasRichText is false because the class has still not assigned the correct value to it.
When it(this.HasRichText) is true, you get this._richText.ToString(), that is the formatted number.
So, if you access the Value property before accessing RichText you should get the correct value, anyway you can get _cellValue using reflection, then convert it to double like this:
var theRealDoubleValue = Convert.ToDouble(yourCell.GetType().GetField("_cellValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(yourCell));

Categories