This part of code is responsible to capture user input from keyboard and use it. When i press some button (ex. C) on keyboard variable TAG receives this as object (byte) value 3. i cannot find out why debugger returns the following error:
System.InvalidCastException. Specified cast is not valid.
num and tag declared as integer value. what is wrong? In this line int? tag = (int?) this.pnlAnswers.Controls[num.Value].Tag; - debugger points to .Tag at the end of line as error.
private void Question_KeyDown(object sender, KeyEventArgs e)
{
int? num = null;
this.qta.get_answer_number_by_key(new int?(e.KeyValue), ref num);
if (!num.HasValue)
{
this.SwitchQuestion(e.KeyValue);
}
else
{
num -= 1;
bool? nullable2 = false;
bool? end = false;
if (this.pnlAnswers.Controls.Count >= (num + 1))
{
Valid valid;
int? tag = (int?) this.pnlAnswers.Controls[num.Value].Tag;
this.qta.test_answer(this.q, tag, ref nullable2, ref end, ref this.pass);
this.e = end.Value;
if (nullable2.Value)
{
valid = new Valid(MessageType.Valid);
}
else
{
valid = new Valid(MessageType.Invalid);
}
valid.ShowDialog();
base.Close();
}
}
}
i`ve tried to change
int? tag = (int?) this.pnlAnswers.Controls[num.Value].Tag;
to
byte? tag = (byte?) this.pnlAnswers.Controls[num.Value].Tag;
and error gone, however i have issues with post-processing of receiving this values.
You need to cast the object referenced by Tag property to it's actual type which is byte. Then you can do further conversion against the byte object :
byte tagByte = (byte)this.pnlAnswers.Controls[num.Value].Tag);
int? tag = (int?) tagByte;
//or in short :
//int? tag = (byte)this.pnlAnswers.Controls[num.Value].Tag;
Simple test I did to confirm this behavior :
byte initialValue = 3;
object TAG = initialValue;
int? tagSuccess = (int?)((byte)TAG); //successfully convert TAG to type int?
int? tagFails = (int?)TAG; //throw InvalidCastException
The accepted answer does not always work.
When a Tag is set to 1 in the property editor of your Visual Studio forms designer, it will be assigned string type, that is "1". you can see that in the debugger when the exception occurs. There are quotes.
A string cannot be cast to any numeric type directly. In this case, (byte) will yield the same exception.
A general but dirty solution for tags of type string OR integer is this..
private int TagValue(object cTag)
{
int iTag = 0;
try { iTag = (int)cTag; } catch { iTag = int.Parse((string)cTag); }
return iTag;
}
Related
I'm new to C# but not to programming in general.
I am trying to set add some error checking to my program. There are 3 textboxes and I am trying to make it so that if the text box is left blank, it assumes a value of 0. Here is my code so far:
private void btnCalculate_Click(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(txtNumberOfClassATix.Text)) // Assumes 0 if no number entered for txtNumberOfClassATix.Text.
{
txtNumberOfClassATix.Text = "0";
}
if (String.IsNullOrEmpty(txtNumberOfClassBTix.Text)) // Assumes 0 if no number entered for txtNumberOfClassBTix.Text.
{
txtNumberOfClassBTix.Text = "0";
}
if (String.IsNullOrEmpty(txtNumberOfClassCTix.Text)) // Assumes 0 if no number entered for txtNumberOfClassCTix.Text.
{
txtNumberOfClassCTix.Text = "0";
}
int classANum = int.Parse(txtNumberOfClassATix.Text);
int classBNum = int.Parse(txtNumberOfClassBTix.Text);
int classCNum = int.Parse(txtNumberOfClassCTix.Text);
double classATotal = classANum * classAPrice;
double classBTotal = classBNum * classBPrice;
double classCTotal = classCNum * classCPrice;
lblCalculatedClassARevenue.Text = $"{classATotal:c}";
lblCalculatedClassBRevenue.Text = $"{classBTotal:c}";
lblCalculatedClassCRevenue.Text = $"{classCTotal:c}";
lblCalculatedTotalRevenue.Text = $"{(classATotal + classBTotal) + classCTotal:c}";
}
This code works but I'm sure I could replace those if statements with something simpler. I've seen how to set a variable to null if another is null using the null-conditional operator but I don't really grasp it enough to adapt it to my scenario.
So far maccettura's answer is the best, but can we do better? Sure we can. Let's make a general-purpose extension method:
internal static class Extensions
{
public static int? AsInt(this string s)
{
int result;
if (s == null)
return null;
else if (int.TryParse(s, out result))
return result;
else
return null;
}
}
And now:
int classANum = txtNumberOfClassATix.Text.AsInt() ?? 0;
If it's an int, you get the int. If it's not, you get zero. Easy peasy.
Or, you might want this extension method:
internal static class Extensions
{
public static int AsInt(this string s, int default = 0)
{
int result;
if (s == null)
return default;
else if (int.TryParse(s, out result))
return result;
else
return default;
}
}
And now you can say what you want the default to be without using ??.
This style of programming is called "fluent programming"; it can make code that is very easy to read and understand.
Notice that this solution does not update the UI with zeros; if you wanted to do that then I would recommend splitting that into two steps: one which causes the mutation, and then a separate step which computes the value. Operations which are useful for both their effects and their values can be confusing.
This is a perfect time to use a method so you arent repeating yourself:
private static int GetInputAsInt(TextBox textbox)
{
int outputValue = 0;
if(textbox?.Text != null && int.TryParse(textbox.Text, out outputValue))
{
return outputValue;
}
return 0;
}
Now you are checking if the textbox itself is not null, and that the value contained therein is a int, if anything fails it returns a 0;
Call it in your other method like this:
int classANum = GetInputAsInt(txtNumberOfClassATix);
Which means your button click event would be a bit simpler:
private void btnCalculate_Click(object sender, EventArgs e)
{
int classANum = GetInputAsInt(txtNumberOfClassATix);
int classBNum = GetInputAsInt(txtNumberOfClassBTix);
int classCNum = GetInputAsInt(txtNumberOfClassCTix);
double classATotal = classANum * classAPrice;
double classBTotal = classBNum * classBPrice;
double classCTotal = classCNum * classCPrice;
lblCalculatedClassARevenue.Text = $"{classATotal:c}";
lblCalculatedClassBRevenue.Text = $"{classBTotal:c}";
lblCalculatedClassCRevenue.Text = $"{classCTotal:c}";
lblCalculatedTotalRevenue.Text = $"{(classATotal + classBTotal) + classCTotal:c}";
}
To keep it simple, a good approach is to use the conditional operator. The full example is below (broken across two lines for readability):
txtNumberOfClassATix.Text =
String.IsNullOrEmpty(txtNumberOfClassATix.Text) ? "0" : txtNumberOfClassATix.Text;
This is a nice, readable, assignment for the first part:
myString = ...
The conditional operator breaks down by providing a boolean expression (true/ false) on the left side of the ?. So, for example:
myString = anotherString == "" ? ... // checking if another string is empty
The final part is the :. To the left is the assignment if the expression is true, and to the right goes the assignment if the expression is false. To finish the example:
myString = anotherString == "" ? "anotherString is empty" : "anotherString is not empty";
The above example can be written out in full to clear up any misunderstanding as:
if (anotherString == "")
{
myString = "anotherString is empty";
}
else
{
myString = "anotherString is not empty";
}
This can apply to all the statements. The documentation is found here.
The best way to reduce the line of code is use the function for your common operation(s). In your case, you can create function which checks whether or not the object is NULL or empty. Based on the return value of that function you can proceed ahead. On the other hand, you can handle it on front-end by using different validators such as RequiredFieldValidator, CustomValidator, etc.
I am trying to convert the object to int. I´ve already tried the Convert.ToInt32(object), but it doesn´t work. I don´t understand
what I´m missing. I´m glad, when someone can help.
Heres the code:
void Update()
{
int markNr = 0;
if (this.m_TuioManager.IsConnected
&& this.m_TuioManager.IsMarkerAlive(this.MarkerID) && isTrue)
{
//UserDetected();
TUIO.TuioObject marker = this.m_TuioManager.GetMarker(this.MarkerID);
//int object1 = Int32.TryParse(marker, out markNr);
Debug.Log("The object:" + marker.ToString());
int object1 = Convert.ToInt32(marker);
try
{
if (markNr == object1)
{
Debug.Log("It Worksssss!!!");
presentationCube.newGame();
}
}
catch (Exception)
{
Debug.Log("Failed restarting the game");
}
Only objects that represent an integer can be converted to an integer. TuioObject is not a representation of an integer, but it may contain one or more integers or values which can be converted to an integer.
What I suggest you do, is to add a function to TuioObject, which returns the integer you need. For example if the number you want to compare is an id:
public int GetId ()
{
return id;
}
Then replace
int object1 = Convert.ToInt32(marker);
with
int id = marker.GetId();
If TuioObject is in an external framework, which you cannot edit, you can create a subclass of TuioObject with the new function.
There are a few other Questions on how to convert Enums and what happens if the value parsed is out-of-range, like in:
public enum SomeTypes
{
SomeType1 = 1,
SomeType2 = 2,
SomeType3 = 3
}
public class SomeClass
{
...
var inRange = (SomeTypes) 1;
var outOfRange = (SomeTypes) 5;
...
}
Going out-of-range will not produce any error.
But I found the hard way that if you try to serialize-deserialize an enum with an out-of-range value you'll get weird errors. For example, I was getting something like
"error parsing the message or timeout exceeded"
which kept me looking for other reasons than the enum out-of-range.
Suggestions to handle this are by the means of the Enum.IsDefined. That seems to work quite nicely, but then there's this rather bold warning on msdn:
"Do not use System.Enum.IsDefined(System.Type,System.Object) for enumeration range checks as it is based on the runtime type of the enumeration, which can change from version to version."
So, my question is, can we safely use Enum.IsDefined or what is the correct way to check if the value of an enum is out-of-range without using the Enum.IsDefined?
Use Enum.GetValues():
public bool IsInRange(int value){
var values = Enum.GetValues(typeof(SomeTypes)).Cast<int>().OrderBy(x => x);
return value >= values.First() && value <= values.Last();
}
[EDIT]
In case you want to check if the item is defined instead of just checking if it's inside the range, you can do similarly:
public bool IsDefined(int value){
var values = Enum.GetValues(typeof(SomeTypes)).Cast<int>().OrderBy(x => x);
return values.Contains(value);
}
There is an option for something simpler:
int value;
bool isInRange = string.IsNullOrEmpty(Enum.GetName(typeof(myEnumType), value));
I did a similar thing with objects DataContract
You must decorate the items in the list with [EnumMember] and then you could obtain the enum name with this method. So you would know if value exists in the enum cos returns his enum name.
public static string GetEnumNameFromValue(System.Type typeEnum, string value)
{
FieldInfo[] fis = typeEnum.GetFields();
foreach (FieldInfo fi in fis)
{
EnumMemberAttribute[] attributes = (EnumMemberAttribute[])fi.GetCustomAttributes(typeof(EnumMemberAttribute), false);
if (attributes.Length > 0)
{
if (string.Compare(attributes[0].Value, value, true) == 0)
{
return fi.Name;
}
}
}
return null;
}
I have some factory code that creates objects based on the value of a class member representing an enum:
public enum BeltPrinterType
{
None,
ZebraQL220,
ONiel
// add more as needed
}
public static BeltPrinterType printerChoice = BeltPrinterType.None;
public class BeltPrinterFactory : IBeltPrinterFactory
{
// http://stackoverflow.com/questions/17955040/how-can-i-return-none-as-a-default-case-from-a-factory?noredirect=1#comment26241733_17955040
public IBeltPrinter NewBeltPrinter()
{
switch (printerChoice)
{
case BeltPrinterType.ZebraQL220:
return new ZebraQL220Printer();
case BeltPrinterType.ONiel:
return new ONielPrinter();
default:
return new None();
}
}
}
I need to set the value of printerChoice before I call NewBeltPrinter() - that is, if it has been changed from its default "None" value. So I'm trying to assign that value based on the string representation, but have gotten to the proverbial point of no continuance with this attempt:
string currentPrinter = AppSettings.ReadSettingsVal("beltprinter");
Type type = typeof(PrintUtils.BeltPrinterType);
foreach (FieldInfo field in type.GetFields(BindingFlags.Static | BindingFlags.Public))
{
string display = field.GetValue(null).ToString();
if (currentPrinter == display)
{
//PrintUtils.printerChoice = (type)field.GetValue(null);
PrintUtils.printerChoice = ??? what now ???
break;
}
}
I've tried everything I could think of, and have been repaid with nothing but constant reprimands from the compiler, which has pretty much been dis[mis]sing me as a knave and a sorry rascal.
Does anybody know what I should replace the question marks with?
What about just this instead of your second code block:
PrintUtils.printerChoice = (PrintUtils.BeltPrinterType)
Enum.Parse(typeof(PrintUtils.BeltPrinterType),
AppSettings.ReadSettingsVal("beltprinter"));
Use Enum.Parse to convert a string into the enum value.
(or Enum.TryParse to attempt to do it without raising an exception if it didn't parse)
edit
If you don't have an Enum.Parse available, then you will have to do the conversion yourself:
switch (stringValue)
{
case "BeltPrinterType.ONiel": enumValue = BeltPrinterType.ONiel; break;
...etc...
}
I can't compile to .NET 1.1 but this seems to work in 2.0
PrintUtils.printerChoice = (BeltPrinterType)field.GetValue(null);
EDIT : I just realized this was basically a comment in your code... But yeah I really don't see why this wouldn't work even in 1.1
From the Smart Device Framework 1.x code base:
public static object Parse(System.Type enumType, string value, bool ignoreCase)
{
//throw an exception on null value
if(value.TrimEnd(' ')=="")
{
throw new ArgumentException("value is either an empty string (\"\") or only contains white space.");
}
else
{
//type must be a derivative of enum
if(enumType.BaseType==Type.GetType("System.Enum"))
{
//remove all spaces
string[] memberNames = value.Replace(" ","").Split(',');
//collect the results
//we are cheating and using a long regardless of the underlying type of the enum
//this is so we can use ordinary operators to add up each value
//I suspect there is a more efficient way of doing this - I will update the code if there is
long returnVal = 0;
//for each of the members, add numerical value to returnVal
foreach(string thisMember in memberNames)
{
//skip this string segment if blank
if(thisMember!="")
{
try
{
if(ignoreCase)
{
returnVal += (long)Convert.ChangeType(enumType.GetField(thisMember, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase).GetValue(null),returnVal.GetType(), null);
}
else
{
returnVal += (long)Convert.ChangeType(enumType.GetField(thisMember, BindingFlags.Public | BindingFlags.Static).GetValue(null),returnVal.GetType(), null);
}
}
catch
{
try
{
//try getting the numeric value supplied and converting it
returnVal += (long)Convert.ChangeType(System.Enum.ToObject(enumType, Convert.ChangeType(thisMember, System.Enum.GetUnderlyingType(enumType), null)),typeof(long),null);
}
catch
{
throw new ArgumentException("value is a name, but not one of the named constants defined for the enumeration.");
}
//
}
}
}
//return the total converted back to the correct enum type
return System.Enum.ToObject(enumType, returnVal);
}
else
{
//the type supplied does not derive from enum
throw new ArgumentException("enumType parameter is not an System.Enum");
}
}
}
Well the question title may not be self explanatory, so let me go ahead and elaborate.
Consider, a TextBox that accepts only numeric value or is left empty. The value(text) entered is stored in an integer(int32) variable. The problem arises when the user enters the digit 0 or leaves the TextBox empty, as the conversion from string to int, converts an empty string to "0" as well.
So my question stands: How do I differentiate the 2 scenarios?
EDIT I figured a lot of questions may be answered by the code and exact problem(as I see it)
if (txtOtherId.Text == string.Empty)
{
otherId = Convert.ToInt32(null);
}
else
{
otherId = Convert.ToInt32(txtOtherId.Text);
}
How about an extension method?
public static class Extensions
{
public static bool TryGetInt(this TextBox tb, out int value)
{
int i;
bool parsed = int.TryParse(tb.Text, out i);
value = i;
return parsed;
}
}
Usage:
int i;
if (textBox1.TryGetInt(out i))
{
MessageBox.Show(i.ToString());
}
else
{
// no integer entered
}
What have you tried? Can we see your code?
Now, I tried the following:
int i;
i = Convert.ToInt32(""); // throws, doesn't give zero
i = int.Parse(""); // throws, doesn't give zero
bool couldParse = int.TryParse("", out i); // makes i=0 but signals that the parse failed
So I can't reproduce. However, if I use null instead of "", the Convert.ToInt32 does convert into zero (0). However, Parse and TryParse still fail with null.
UPDATE:
Now that I see your code. Consider changing the type of otherId from int to int? where the question mark makes it a nullable type. Then:
if (txtOtherId.Text == "")
{
otherId = null; // that's null of type int?
}
else
{
otherId = Convert.ToInt32(txtOtherId.Text); // will throw if Text is (empty again or) invalid
}
If you want to be sure no exceptions can happen, do this:
int tmp; // temporary variable
if (int.TryParse(txtOtherId.Text, out tmp))
otherId = tmp;
else
otherId = null; // that's null of type int?; happens for all invalid input
You could use a nullable int, and then have blank string be null.
int? myValue = String.IsNullOrEmpty(myTextbox.Text)
? (int?)null
: int.Parse(myTextbox.Text);
For clarity, the above is equivalent to
int? myValue = null;
if(!String.IsNullOrEmpty(myTextbox.Text))
{
myValue = int.Parse(myTextbox.Text);
}
Assuming that it is indeed a textbox...
string result = myTextBox.Text;
if (string.IsNullOrEmpty(result))
// This is an empty textbox
else
// It has a number in it.
int i = int.Parse(result);
There're 2 simple approaches how to do it:
string inputText="";
int? i=null;
if (!string.IsNullOrWhiteSpace(inputText))
i = int.Parse(inputText);
int i2;
bool ok = int.TryParse(inputText, out i2);