For example if both values are int type it adds them.... ie 2+2=4
if both values are float....ie 2.2+2.3=4.5
or if one value is string and second is int...ie 1 + Pak=1Pak
We will get these two values from user using tfwo textboxes
This would be one way of doing it. Without having to convert to string and than back to numeric.
public object Add(IConvertible a, IConvertible b)
{
if(IsNumeric(a) && IsNumeric(b))
return a.ToDouble(CultureInfo.InvariantCulture) + b.ToDouble(CultureInfo.InvariantCulture);
return a.ToString() + b.ToString();
}
public static bool IsNumeric(object o)
{
var code = (int)Type.GetTypeCode(o.GetType());
return code >= 4 && code <= 15;
}
You can't do it using generics. You'll receive string from your textbox anyway. The only thing to do is to implement it "manually" exactly this way as you said:
public string TrySumTwoStrings(string input1, string input2)
{
double numeric1, numeric2;
if (Double.TryParse(input1, out numeric1) && Double.TryParse(input2, out numeric2))
return (numeric1 + numeric2).ToString();
return input1 + input2;
}
There's no way to use generics if we have no different types (everything is typed as string here).
You wouldn't, generics cannot be constrained in a way to support arithmetic operators (or concatenation). You would need to create overloads.
public int Add(int x, int y)
public double Add(double x, double y)
public decimal Add(decimal x, decimal y)
// etc.
Of course, you still have the problem of determining how exactly to parse your data. The source being a TextBox, the data will inherently be strings. You will have to determine which type of number it should be, if any.
If doing this for a real application, you shouldn't have this problem. Your textbox should be expected to receive input from the user in the form of an integer, or a decimal, or a string, etc. If it's not convertible to the proper type, it's an invalid input from your user. You wouldn't want the input to have to be magically deduced.
string Str1 = textBox1.Text.Trim();
string Str2 = textBox2.Text.Trim();
double Num1,num2;
bool isNum1 = double.TryParse(Str1, out Num1);
bool isNum2 = double.TryParse(Str2, out Num2);
if (isNum1 && isNum2)
MessageBox.Show((isNum1 + isNum2).ToString());
else
MessageBox.Show( Str1 + Str2);
Check out http://www.yoda.arachsys.com/csharp/miscutil/ The MiscUtil library. It contains some very clever Expression Tree stuff to allow operators with generics. It's not going to work exactly how you want (as others have stated, you can't constrain types to have operators) but it should do exactly what you want.
I don't know how it handles adding different types together though, I've not tried that.
I would think it would take some processing the values before hand in order with things like String.PArse, Int.Parse, etc...
Take them in order of compplexity first because 1 will convert to string, however x will not convert to integer or float.
Officially changed my answer to same as comment...
Best suggestion I have on that would be allow the user to select what type to interpret the data as and pass to the appropriate function based on what the user meant, there would be too many ways to interprets char strings to know what the users intention was, code processes logic not intent.
Related
Hello I'm making a quiz app and I want to compare user answers to the correct answers. I'm planning to do that with two string lists as I collect the answers in strings and I also have the correct answers in strings.
The problem that I'm facing is that half of the questions are True/False questions so the answers are in "True" or "False" strings, the other half are questions where a calculation has to be performed, therefore the answer is a number/double which I store as a string.
I want to give a range of answer acceptance of 0.1 so if the correct answer is 22.5 an answer of 22.6 would still be considered correct. This, however, makes it impossible to compare the value of the "number" strings with the Equals() method.
So I'm ideally looking for an if statement where I want to say:
if the element is convertible to a double convert it and check if its value is the same as the correct answer or within the acceptance range
else, check if the element is equal to the correct answer
For now I have made a console app to try and solve this problem where I have defined the two lists which look like this:
static void Main(string[] args)
{
List<string> givenAnswers = new List<string>()
{
"True","False","True","False","True",
"60","50","2.321","0.8","1.55"
};
List<string> correctAnswers = new List<string>()
{
"True","False","False","True","False",
"70","20","1.231","0.5","1.25"
};
correctAnswers.ForEach(Console.WriteLine);
Console.ReadLine();
}
The main problem is that c# doesnt return false if the element is not convertible but gives error which breaks the program.
(I was previously making the comparison on my frontend - java script where an unsuccessful conversion would return false)
Let's extract a method to compare two answers:
private static bool IsCorrectAnswer(string answer,
string expectedAnswer,
double tolerance = 0.1) {
// Let's not be peevish and tolerate leading / trimming spaces,
// ignore cases i.e. let " TRUE " be equal to "True"
if (string.Equals(answer?.Trim(),
expectedAnswer?.Trim(),
StringComparison.OrdinalIgnoreCase))
return true;
// If we have two double values, let's to parse them and compare
// with provided tolerance
if (double.TryParse(answer, out var leftValue) &&
double.TryParse(expectedAnswer, out var rightValue))
return Math.Abs(leftValue - rightValue) <= tolerance;
//TODO: you may want to add support for other types (say, dates) here
return false;
}
I didn't get your string list of booleans and numbers, but the general check would look like this:
foreach (var answer in givenAnswers)
{
if (double.TryParse(answer, NumberStyles.Any, CultureInfo.InvariantCulture, out double answerVal) && answerVal == 0.8)
{
Console.WriteLine("Nailed it!");
}
}
I had to use "invariant culture" because in my country, 0.8 is written as "0,8" and thus "0.8" is parses to 8...
I wouldn't go into lengths about the "nearby" numbers, because it's very complicated. Comparing Floating Point Numbers, 2012 Edition
You can use double.TryParse to check if a string is parsable to a double and parse it at the same time when it is.
bool IsAcceptableAnswser(string givenAnswer, string correctAnswer)
{
if (double.TryParse(correctAnswer, out var correctNumber) && double.TryParse(givenAnswer, out var givenNumber))
{
// When the answers are doubles, use use the double values.
var diff = Math.Abs(correctNumber - givenNumber);
return diff <= 0.1;
}
else
{
// When the answers aren't doubles, use string.Equals.
return string.Equals(givenAnswer, correctAnswer);
}
Notes
I used string.Equals for simplicity, but you could of course use something else to be more permissive around casing.
You might also want to check the other overloads of double.TryParse if culture is an issue.
I used 0.1 directly for readability. It would be preferable to use a constant or receive it as parameter.
Try:
var convertible = int.TryParse("60", out_)
I'm wondering what the correct way to compare two characters ignoring case that will work for all cultures. Also, is Comparer<char>.Default the best way to test two characters without ignoring case? Does this work for surrogate-pairs?
EDIT: Added sample IComparer<char> implementation
If this helps anyone this is what I've decided to use
public class CaseInsensitiveCharComparer : IComparer<char> {
private readonly System.Globalization.CultureInfo ci;
public CaseInsensitiveCharComparer(System.Globalization.CultureInfo ci) {
this.ci = ci;
}
public CaseInsensitiveCharComparer()
: this(System.Globalization.CultureInfo.CurrentCulture) { }
public int Compare(char x, char y) {
return Char.ToUpper(x, ci) - Char.ToUpper(y, ci);
}
}
// Prints 3
Console.WriteLine("This is a test".CountChars('t', new CaseInsensitiveCharComparer()));
It depends on what you mean by "work for all cultures". Would you want "i" and "I" to be equal even in Turkey?
You could use:
bool equal = char.ToUpperInvariant(x) == char.ToUpperInvariant(y);
... but I'm not sure whether that "works" according to all cultures by your understanding of "works".
Of course you could convert both characters to strings and then perform whatever comparison you want on the strings. Somewhat less efficient, but it does give you all the range of comparisons available in the framework:
bool equal = x.ToString().Equals(y.ToString(),
StringComparison.InvariantCultureIgnoreCase);
For surrogate pairs, a Comparer<char> isn't going to be feasible anyway, because you don't have a single char. You could create a Comparer<int> though.
As I understand it, there isn't really a way that will "work for all cultures". Either you want to compare characters for some kind of internal, non-displayed-to-the-user reason (in which case you should use the InvariantCulture), or you want to use the CurrentCulture of the user. Obviously, using the user's current culture will mean that you will get different results in different locales, but they will be consistent with what your users in those locales will expect.
Without knowing more about WHY you are comparing two characters, I can't really advise you on which one you should be using.
I would recommend comparing uppercase, and if they don't match then comparing lowercase, just in case the locale's uppercasing and lowercasing logic behave slightly different.
Addendum
For example,
int CompareChar(char c1, char c2)
{
int dif;
dif = char.ToUpper(c1) - char.ToUpper(c2);
if (diff != 0)
dif = char.ToLower(c1) - char.ToLower(c2);
return dif;
}
What I was thinking that would be available within the runtime is something like the following
public class CaseInsensitiveCharComparer : IComparer<char> {
private readonly System.Globalization.CultureInfo ci;
public CaseInsensitiveCharComparer(System.Globalization.CultureInfo ci) {
this.ci = ci;
}
public CaseInsensitiveCharComparer()
: this(System.Globalization.CultureInfo.CurrentCulture) { }
public int Compare(char x, char y) {
return Char.ToUpper(x, ci) - Char.ToUpper(y, ci);
}
}
// Prints 3
Console.WriteLine("This is a test".CountChars('t', new CaseInsensitiveCharComparer()));
You could try:
class Test{
static int Compare(char t, char p){
return string.Compare(t.ToString(), p.ToString(), StringComparison.CurrentCultureIgnoreCase);
}
}
But I doubt this is the "optimal" way to do it, but I'm not all of the cases you need to be checking...
string.Compare("string a","STRING A",true)
It will work for every string
I know this is an old post, but things have changed since then.
The question above can be answered by using an extension. This would extend the char.Equals to allow for locality and case insensitivity.
In an extension class, add something such as:
internal static Boolean Equals(this Char src, Char ch, StringComparison comp)
{
Return $"{src}".Equals($"{ch}", comp);
}
I'm currently at work, so can't check this, but it should work.
Andy
You can provide last argument as true for caseInsensetive match
string.Compare(lowerCase, upperCase, true);
I have made a little calculator:
private void button1_Click(object sender, EventArgs e)
{
int input1 = int.Parse(textBox1.Text);
int input2 = int.Parse(textBox2.Text);
int Result = input1 + input2;
textBox3.Text = Result.ToString();
}
Why can't I just do textBox3.Text = Result; when I already told them that the result is int type in the line before?
When I need to explain data types, I often use the analogy with shapes.
A data type is a shape, that forms the behaviour of the variable of the specified type. When a variable of a given type is created it holds the description of the type and a value. Variables with the same shape can be connected together with the help of operators (for example +).
Two variables from a different type (shape) can not be connected directly together - they need a converter / wrapper.
A converter makes for example from the shape triangle a circle or a wrapper masks the triangle as a circle.
Back to your example. TextBox controls can hold only the data type string, meaning they are incompatible with the shape of an int. You already have converted the content of the string to an int using int input1 = int.Parse(textBox1.Text);.
That is why you can not simply assign the Result variable to the Text property - their shapes are different, so you need to convert the int back to string using the ToString() method. You could also write textBox3.Text = (input1 + input2).ToString();.
The shape analogy can be used to understand parameters passing - as long you know the signature of a method int add(int a, int b), you know exactly what you need to put when you are calling a method (two int's) and what kind of type to expect as a result (an int). For example String result = add(1,2); will not work because the signature is not as expected, result should be of type int in order to work (int result = add(1,2);) or the call to add(1,2) should be converted to string (string result = add(1,2).ToString();).
Same goes with classes and objects - a class is a custom data type, so a custom shape. As long two instances of a class (objects) have the same shape they can be connected or assigned to each other.
This is a very simplified explanation (but still it helped my trainee in the past) so take your time and have a look at the C# MSDN documentation for a thorough explanation of data types.
Why can't I just do textBox3.Text = Result; when I already told them
that the result is int type in the line before?
Even though you 'told' that the result would be of type int, the compiler needs to know how to interpret/transform the string into an int.
int.Parse is one way of doing just that.
Sounds like you may want to start here with C# Data Types. As for your question. TextBox.Text is of type string. Check out MSDN articles for the different controls that you're using and their properties, such as this MSDN Article on TextBox.Text
textBox3.Text is a type of string while Result is a type of int and in your code you are assigning int value to a string which is wrong. You need to transform ones type to the other.
I'm trying to make a basic calculator in c#.
The only problem is, I don't know how I could add numbers to an int; for instance, if I wanted button1 to do something like this in a textbox, it'd be
textBox1.text += "1"
but this is for the operations, and the textbox displays the operator, so I couldn't convert it to an int. I'd really appreciate some help.
You can do it with something like (where s is a string):
s = (Int32.Parse(s) + 1).ToString();
Just make sure that s is actually a valid number, otherwise you'll have to cobble together something with TryParse and figure out what to do when it's not a number, like leave it alone:
int val;
if (Int32.TryParse(s, out val)) {
val++;
s = val.ToString();
}
You can also restrict user input so that they can only enter integers, have a look at MaskedTextBox and set the Mask property. See the documentation here.
C# is a strongly typed language. A textbox contains a string, which you must convert to an int before performing arithmetical operations.
Converting a string to an int can be done with int.Parse(), then you must convert back to a string to change the textbox contents:
int temp = int.Parse(textBox1.Text) + 1;
textBox1.Text = temp.ToString();
This will throw an exception if textBox.Text cannot be converted to an int. To deal with this, look up the int.TryParse() function.
I'm wondering what the correct way to compare two characters ignoring case that will work for all cultures. Also, is Comparer<char>.Default the best way to test two characters without ignoring case? Does this work for surrogate-pairs?
EDIT: Added sample IComparer<char> implementation
If this helps anyone this is what I've decided to use
public class CaseInsensitiveCharComparer : IComparer<char> {
private readonly System.Globalization.CultureInfo ci;
public CaseInsensitiveCharComparer(System.Globalization.CultureInfo ci) {
this.ci = ci;
}
public CaseInsensitiveCharComparer()
: this(System.Globalization.CultureInfo.CurrentCulture) { }
public int Compare(char x, char y) {
return Char.ToUpper(x, ci) - Char.ToUpper(y, ci);
}
}
// Prints 3
Console.WriteLine("This is a test".CountChars('t', new CaseInsensitiveCharComparer()));
It depends on what you mean by "work for all cultures". Would you want "i" and "I" to be equal even in Turkey?
You could use:
bool equal = char.ToUpperInvariant(x) == char.ToUpperInvariant(y);
... but I'm not sure whether that "works" according to all cultures by your understanding of "works".
Of course you could convert both characters to strings and then perform whatever comparison you want on the strings. Somewhat less efficient, but it does give you all the range of comparisons available in the framework:
bool equal = x.ToString().Equals(y.ToString(),
StringComparison.InvariantCultureIgnoreCase);
For surrogate pairs, a Comparer<char> isn't going to be feasible anyway, because you don't have a single char. You could create a Comparer<int> though.
As I understand it, there isn't really a way that will "work for all cultures". Either you want to compare characters for some kind of internal, non-displayed-to-the-user reason (in which case you should use the InvariantCulture), or you want to use the CurrentCulture of the user. Obviously, using the user's current culture will mean that you will get different results in different locales, but they will be consistent with what your users in those locales will expect.
Without knowing more about WHY you are comparing two characters, I can't really advise you on which one you should be using.
I would recommend comparing uppercase, and if they don't match then comparing lowercase, just in case the locale's uppercasing and lowercasing logic behave slightly different.
Addendum
For example,
int CompareChar(char c1, char c2)
{
int dif;
dif = char.ToUpper(c1) - char.ToUpper(c2);
if (diff != 0)
dif = char.ToLower(c1) - char.ToLower(c2);
return dif;
}
What I was thinking that would be available within the runtime is something like the following
public class CaseInsensitiveCharComparer : IComparer<char> {
private readonly System.Globalization.CultureInfo ci;
public CaseInsensitiveCharComparer(System.Globalization.CultureInfo ci) {
this.ci = ci;
}
public CaseInsensitiveCharComparer()
: this(System.Globalization.CultureInfo.CurrentCulture) { }
public int Compare(char x, char y) {
return Char.ToUpper(x, ci) - Char.ToUpper(y, ci);
}
}
// Prints 3
Console.WriteLine("This is a test".CountChars('t', new CaseInsensitiveCharComparer()));
You could try:
class Test{
static int Compare(char t, char p){
return string.Compare(t.ToString(), p.ToString(), StringComparison.CurrentCultureIgnoreCase);
}
}
But I doubt this is the "optimal" way to do it, but I'm not all of the cases you need to be checking...
string.Compare("string a","STRING A",true)
It will work for every string
I know this is an old post, but things have changed since then.
The question above can be answered by using an extension. This would extend the char.Equals to allow for locality and case insensitivity.
In an extension class, add something such as:
internal static Boolean Equals(this Char src, Char ch, StringComparison comp)
{
Return $"{src}".Equals($"{ch}", comp);
}
I'm currently at work, so can't check this, but it should work.
Andy
You can provide last argument as true for caseInsensetive match
string.Compare(lowerCase, upperCase, true);