C# A problem with return T
I have a method that converts a number from string to the T, e.g int
public static T GetItemIdFromUrl<T>(string itemName)
{
try
{
...
int ad_id = int.Parse(stringNumber);
return (T)(object)ad_id;
}
catch
{
return (T)(object)-1;
}
}
but as a result I have a hex code 0xff开发者_运维技巧ffffa5
instead of 91
. Why ?
Well, it's hard to know for sure why it's returning 0xffffffa5 without knowing the input string, but your code is going to throw an exception if T is anything other than either int
or a enum with an underlying base type of int
. For example, it will throw if T is double
. Your method isn't really generic - the value it's trying to return is always an int
. Why is your method not just declared to return int
? If you want to convert from int
to some other type (double
, long
, whatever) just let that conversion be done on assignment. The unboxing conversion in your code will not work unless T
is int
or an appropriate enum (the latter of which seems unlikely).
One thing to note is that 0xffffffa5 is the bit pattern for -91. Is it possible that you've only seen that result in the debugger, and the result is meant to be -91 rather than 91?
Even if your intention is to genericize the handling of different numeric types (e.g., double
, decimal
, etc.), this isn't the way to do it. When you box to object
, and then unbox to T
, it will only work if the boxed type actually is T
. In other words, it will not perform a type conversion for you.
The only way (that I know of) to do the type conversion from object
to whatever you want is by using the Convert
class:
return (T)Convert.ChangeType(ad_id, typeof(T));
Even this is fragile, obviously (as it won't work if T
is any non-numeric type); but at least it won't throw exceptions in pretty much all cases you're trying to cover. (Your method as it is currently implemented will throw for every T
except int
.)
I think you have a fundamental misunderstanding of what generics are and how they're supposed to be used. The whole point of generics is to have the benefit of working on different types while not losing type safety which is what happens when you cast to object
That being said Jon Skeet is probably correct that it's probably the way the debugger is displaying the result.
I think you are doing something else where you show the ellipses (...):
try
{
...
int ad_id = int.Parse(stringNumber);
return (T)(object)ad_id;
}
because I tried your sample and I get a return int
.
You really don't have any way, currently, of ensuring your return is going to work. You method will rely on very specific information. Provide a bit more info where you have the ellipses and a better solution can be recommended. As Jon mentions, a nullable int? would be better. Test for the condition and if it isn't a correct return, you can check for myInt.HasValue
.
I have been pondering a complete solution this problem and came up with the following:
static List<Type> numerics = new List<Type>();
static void Main(string[] args)
{
numerics.Add(typeof(Decimal));
numerics.Add(typeof(Double));
numerics.Add(typeof(Int16));
numerics.Add(typeof(Int32));
numerics.Add(typeof(Int64));
numerics.Add(typeof(Single));
numerics.Add(typeof(UInt16));
numerics.Add(typeof(UInt32));
numerics.Add(typeof(UInt64));
Console.WriteLine("StringNumber 55: {0} {1}", typeof(int), GetItemIdFromUrl<int>("55"));
Console.WriteLine("StringNumber 5B99: {0} {1}", typeof(int), GetItemIdFromUrl<int>("5B99"));
Console.ReadKey();
}
public static T GetItemIdFromUrl<T>(string stringNumber)
{
try
{
if (numerics.Contains(typeof(T)))
return (T)Convert.ChangeType(stringNumber, typeof(T));
else
return default(T);
}
catch (ArgumentException argEx)
{
Console.WriteLine("Exception\r\n{0}", argEx);
return default(T);
}
catch (FormatException formatEx)
{
Console.WriteLine("Exception\r\n{0}", formatEx);
return default(T);
}
}
From the 2nd WriteLine
you will be able to see what happens if an invalid numeric is passed. Of course, I included the Exception
messages just so you could see what they would return. It might not be a glamorous solution, but I think it is viable.
I also came up with this:
public static class GetNumeric<T>
{
public static Func<string, T> Value = stringValue => (T)Convert.ChangeType(stringValue, typeof(T));
}
Although I would still wrap the call in a try/catch
block.
精彩评论