Reputation: 3834
in one of my projects I'm using following two methods. 1. GetDoubleValue and 2. GetIntValue. GetDoubleValue uses double.TryParse to parameter str string and returns 0 if it fails while GetIntValue tries int.TryParse to parameter str string and returns 0 if it fails. What I want is to combine these two methods into one generic method that alongwith string str receives parameter T as well so that if I want to use GetDoubleValue Method I can use double for the parameter T and if I want to use GetIntValue Method I can use Int for the parameter T
public double GetDoubleValue(string str)
{
double d;
double.TryParse(str, out d);
return d;
}
public int GetIntValue(string str)
{
int i;
int.TryParse(str, out i);
return i;
}
Note: I have tried something like this;
private T GetDoubleOrIntValue<T>(string str) where T : struct
{
T t;
t.TryParse(str, out t);
return t;
}
EDIT
In my database I have more than 30 columns in differenct tables having numeric datatype. I want to insert 0 in each column if user does not type anything in the textbox i.e he leaves all or some of the textboxes empty. If I don't use the GetIntValue method I will have to use the method body more than 30 times. that is why I am doing this through method approach. I'm writing three of more than thirty examples for instance
cmd.Parameters.Add("@AddmissionFee", SqlDbType.Decimal).Value = GetIntValue(tbadmissionfee.Text);
cmd.Parameters.Add("@ComputerFee", SqlDbType.Decimal).Value = GetIntValue(tbcomputerfee.Text);
cmd.Parameters.Add("@NotesCharges", SqlDbType.Decimal).Value = GetDoubleValue(tbnotescharges.Text);
I want to combine the aforesaid two methods because today I'm having two methods like this which if combined will not give any better improvement in the programming but tomorrow I may have tens of methods like this that will better be combined into one generic method. e.g I may have GetInt32Value, GetShortValue etc. hope it is now cleared why I want this???
Upvotes: 25
Views: 22962
Reputation: 1
What about an extension method ?
public static class Extensions
{
public static Nullable<T> TryParse<T>(this String str) where T:struct
{
try
{
T parsedValue = (T)Convert.ChangeType(str, typeof(T));
return parsedValue;
}
catch { return null; }
}
}
usage :
int i = "123".TryParse<int>() ?? 0;
Upvotes: 0
Reputation: 838006
So instead of writing:
double d = GetDoubleValue(str);
You want to be able to write this?
double d = GetValue<double>(str);
Even if you can get it to work, what's the benefit? I personally don't think that's a huge improvement for the client. The only advantage is not having to implement the same method twice. But given the simplicity of the method and the difficulty of implementing this type of code re-use, it seems reasonable here to just duplicate those few lines of code.
You're not alone with this problem. Take a look at other methods in the .NET framework that work on different types and see how they solved it. Here's how BinaryReader
allows you to read different types:
It's not pretty, but that's the way it is typically done.
Regarding your update, I have two further points to make.
You mention that you will likely have more than two types, and this could lead to more duplication of the same code. Given that the method is very concise and simple it doesn't seem a problem to copy and paste the code. It is a reasonable exception to the rule that you shouldn't copy and paste code, in my opinion. The approach here reduces the amount of duplication, though you still need lots of similarly named methods in your public interface.
However I think it's important to mention that it seems wrong to silently use a default value in your situation, as Lee has already mentioned in a comment. If a user makes a mistake while entering data they should receive an error message. You should use some sort of validation framework to ensure that the strings are valid and to inform your user what the problem is if they are not valid. Once you have validated everything it is safe to use int.Parse
instead of int.TryParse
because you know that the parsing will succeed. And if the parsing does fail, that's an exceptional situation (a bug in your validation) so it seems fair that the application bails out with an exception and logs a stack trace. This will help you find and fix the bug.
Upvotes: 8
Reputation: 8292
You could do something like this:
public static T GetDoubleOrIntValue<T>(this string str) where T : IConvertible
{
var thisType = default(T);
var typeCode = thisType.GetTypeCode();
if (typeCode == TypeCode.Double)
{
double d;
double.TryParse(str, out d);
return (T)Convert.ChangeType(d,typeCode) ;
}
else if (typeCode == TypeCode.Int32)
{
int i;
int.TryParse(str, out i);
return (T)Convert.ChangeType(i, typeCode);
}
return thisType;
}
Then when you call it:
string d = "1.1";
string i = "3";
double doubleValue = d.GetDoubleOrIntValue<double>();
int intValue = i.GetDoubleOrIntValue<int>();
But the whole thing seems kinda silly to me.
EDIT: Saw someone else using Convert.ChangeType...which provides for a generic return type.
Upvotes: 8
Reputation: 6101
I've made a simple sample. The code isn't optimal, but it works as expected.
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GetValue<int>("123"));
Console.WriteLine(GetValue<double>("123.123"));
Console.WriteLine(GetValue<DateTime>("2001-01-01 01:01:01"));
}
static T GetValue<T>(string s)
{
var tryParse = typeof (T).GetMethod(
"TryParse", new [] {typeof(string), typeof(T).MakeByRefType()});
if (tryParse == null)
throw new InvalidOperationException();
T t = default (T);
var parameters = new object[] {s, t};
var success = tryParse.Invoke(null, parameters);
if ((bool) success) t = (T)parameters[1];
return t;
}
}
Upvotes: 1
Reputation: 144126
I would consider writing an extension or static method:
delegate bool TryParse<T>(string str, out T value);
public static T GetValue<T>(this string str, TryParse<T> parseFunc)
{
T val;
parseFunc(str, out val);
return val;
}
You then have to provide the TryParse
implementation you want to use:
int i = "1234".GetValue<int>(int.TryParse);
Be warned that this function silently returns a default value if the parse fails. You may want to return default(T)
if the TryParse
delegate returns false.
Upvotes: 12
Reputation: 244757
I agree with Mark Byers. It's probably not a good idea to try making this method generic. A little code duplication won't hurt (as long as it really is only a little). The fact that you could use any struct
with your generic version also doesn't make it look like a good idea to me.
If you really want to do this, you could try using reflection (like Minustar suggested), but that would be both ugly and slow.
Instead, you could use Convert.ChangeType()
:
private T GetValue<T>(string str) where T : struct
{
return (T)Convert.ChangeType(str, typeof(T));
}
Upvotes: 11
Reputation: 7931
As you see you can not restrict where T
to Double
or Int32
, that's why you probably have struct
on that place (int and double are not struct). Value types can not be used as restrictions on generics.
So you can not type safe ensure that TryParse
method exists. You should either typecheck T and throw an exception if it's either double nor int like in answer by @ie.
My suggestion is to use different signature:
private static bool GetValue(string str, out double result)
{
...
}
private static bool GetValue(string str, out int result)
{
...
}
Alternatively you can introduce an interface IParsable
. Box double
or int
result into it and implement implicit conversion into double or integer.
Upvotes: 0
Reputation: 1225
You could try with reflection: first, try to find a "TryParse" method on the type replaced by <T>.
T t;
var typeInfo = typeof(T);
var method = typeInfo.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static);
var result = (bool)method.Invoke(null, new object[] { str, t });
if (result)
return t;
This code is not tested; neither is it optimal performance-wise.
Upvotes: 1