How can one check for “safe” conversions between value types in .NET?
Question
Back to the basics...
For reference types, one can do this:
SomeType someObject = firstObject as SomeType;
if (someObject == null)
{
// Handle the situation gracefully
}
else
{
// Do stuff
}
For value types, my understanding is that we have implicit conversions (no data loss), explicit conversions (needed if there's a risk of data loss), the Convert
class (a "conversion wrapper" I think) and also type-specific conversions (e.g. double x = Double.Parse("2");
), but I haven't found anything similar to the as
operator above.
So, my question is: does the framework provide with some method/operator/technique to do something along these lines:
if (!Convert.CanConvert(someValue, someValueType))
{
// Beware! Data loss can occur
}
else
{
// No data loss here
}
If not, can anyone out there suggest a solid approach to build one such CanConvert
method?
Thanks a lot!
EDIT(1): The user-case/problem is as follows: Given a something passed by the code's consumer (my other self, but that's irrelevant), (1) Check that something is a number (easy enough) and (2) Place something in the "smallest" numeric type where it fits without incurring in data loss.
Some background: the nature of what I'm trying to do is more mathematical than technical: I'm trying to see if/how I can fit existing numeric types into some sort of an algebraic hierarchy of the form Monoid=>Group=>Ring=>Field (or a simplified version thereof). While working on this, and not very sure how, "one thing led to another" and I found myself having to deal with type conversions...
Solution
How about the TryParse method on the various value types?
int x;
if (int.TryParse(someType.ToString(), out x))
return x;
OTHER TIPS
Henk is pretty much on the money. I'd like to add something to his answer, if I would:
Value-type conversion in the .NET Framework works using the IConvertible
interface. The Convert
class makes use of this for almost all of its methods. This is very different from implicit/explicit conversion operators in C#, which are merely another form of syntactic sugar.
If you write this:
public struct Duck
{
public static implicit operator Goose(Duck d)
{
...
}
}
The .NET Framework itself has no idea that this exists. It is emitted as an op_implicit
and it's up to the compiled language to figure out how to use it. Not every MSIL language actually supports these. So this code works:
Goose g1 = duck;
This code doesn't:
Goose g1 = (Goose)Convert.ChangeType(duck, typeof(Goose));
In order to implement a CanConvert
method that is aware of implicit/explicit conversion operators, you would actually have to use Reflection to check for the individual op_
methods, and I'd have to recommend against doing that - I can see little use for it in practice.
Take a look at Convert.ChangeType
. You could hijack that to meet your purposes, though it would be slow due to exception throwing and duplicate conversion.
the "as" keyword is basically a safe downcast. Since all value types are sealed, they cannot be inherited from.
So your code would be:
if (firstObject is MyValueType)
{
MyValueType obj = (MyValueType) firstObject;
}
else
{
}
I think you are misunderstanding the point of the as operator. The as operator is roughly equivalent to the following code:
if (firstObject is SomeType)
return (SomeType)firstObject;
else
return null;
So as is more of an inheritance check. (Such as List implements IList)
Value types do not support inheritance, and for good reason. Double and Int64 both store the number 1 in completely different manners.
Basically what you want is a method that will determine for you whether a number conversion is loseless or not. Well I counter with "Why?". While there are quite a few formats supported by the CLR, the conversion rules are usually pretty simple. For example Int32 -> Double is lossless, and any conversion from a "smaller" to a "larger" is lossless, such as SByte -> Int64.
Another question is, what would a false in your example signify? I would say very little, for example:
Convert.CanConvert(123456789.12345F, typeof(Byte))
Of what use is the false result? You imply it is for cases like Int32 -> Single, where some data would be lost, but in this case a ton of data is being lost, as the "closest" Byte representation is 255.
It is because of these two issues that there is no such method.
The as
operator is based on inheritance, and value types don't inherit. You could probably write a CanConvert()
but it would have to work with Boxed valuetypes, and you normally want to avoid boxing.
So, Possible: Yes, Desirable: No.
Maybe you can add a Use-Case scenario where you want to use this and then we can recommend alternatives.
Re: Edit(1)
I hope you are aware that the system of numerical types is mostly historical baggage and does not follow very logical rules. There is for instance no such thing as a short
calculation, they are always converted to int before doing anything.
But maybe you can define that behaviour in terms of Ring and Field, that Algebra has been a "long time passing" for me.