I'm trying to write a generic method to perform an unchecked assignment from a long to other type. Here is the simplified version:
private static void AssignHex<T>(string hex, out T val) where T : struct
{
if (long.TryParse(hex, NumberStyles.AllowHexSpecifier, null, out long lval))
{
unchecked
{
val = (T)Convert.ChangeType(lval, typeof(T));
}
}
else
{
val = default(T);
}
}
This works fine except for input string "FFFFFFFF" and type int where I expect to get -1 and instead get overflow exception. The equivalent non-generic method works fine:
private static void AssignHex2(string hex, out int val)
{
if (long.TryParse(hex, NumberStyles.AllowHexSpecifier, null, out long lval))
{
unchecked
{
val = (int)lval;
}
}
else
{
val = default(int);
}
}
I can simply write non-generics but it bothers me that I can't get the generic version to work. Any solution?
While System.Convert.ChangeType is really handy a lot of times but you cannot use it in your scenario. FFFFFFFF in decimal is 4294967295 which cannot be represented in int type and conversion fails. Specific code that throws is this (there is a check if long is larger than maximal int value).
If you want this functionality, then you will have to manually write similar code as is in System.Convert.ChangeType - if statements on different possible types with appropriate casts wrapped in unchecked block. Or simply not use generics and have overload for each type that you are interested in.
EDIT: It may be better to remove unchecked blocks altogether and parse hex value directly to appropriate type instead of parsing it first to long. This way you will directly parse it to expected value and receive error if value is out of range instead of just silently ignoring leftover bits with unchecked block.
EDIT2: Negative numbers are represented using two's complement. In short you can get negative number from positive by flipping all bits which represent positive number and adding 1. This means that binary or hex representation of negative number depends on how many bits are allocated for a number. So for 8 bit number (sbyte) -1 is 0xFF, for 16 bit number (short) is 0xFFFF, for 32 bit number (int) is 0xFFFFFFFF and for 64 bit number (long) is 0xFFFFFFFFFFFFFFFF. Since you are parsing hex value 0xFFFFFFFF as long this is not -1, since you are actually parsing 0x0000000FFFFFFFF. What your unchecked block does is that when casting to lower precision number it will just take as many bits as required for lower precision type and discard the rest without any checks. Imagine now that you have 0XF0000000FFFFFFFF. If you parse this as long you get ~ -1 quintillion but with unchecked cast to int you would get -1, totally ignoring the most significant 4 bits.
Ok. Some pain was involved, but here is the answer:
private static bool AssignHex4<T>(string hex, out T val)
{
Type t = typeof(T);
MethodInfo mi = t.GetMethod("TryParse", new Type[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(T).MakeByRefType()});
if (mi != null)
{
object[] parameters = new object[] {hex, NumberStyles.AllowHexSpecifier, null, null};
object result = mi.Invoke(null, parameters);
if ((bool) result)
{
val = (T)parameters[3];
return true;
}
}
val = default(T);
return false;
}
Shout outs are required to a few SO answers that were instrumental in putting this together:
MethodInfo.Invoke with out Parameter
Specifying out params for Type.GetMethod
I'll admit it is not a particularly elegant answer, but I think it is comprehensive and that is what I was looking for.
Consider...
AssignHex4("FF", out byte r1);
AssignHex4("FFFF", out short r2);
AssignHex4("FFFFFFFF", out int r3);
AssignHex4("FFFFFFFFFFFFFFFF", out long r4);
AssignHex4("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", out BigInteger r5);
Console.WriteLine("Convert in reflection function returns:" + r1 + ", " + r2 + ", " + r3 + ", " + r4 + ", " + r5);
results in...
Convert in reflection function returns:255, -1, -1, -1, -1
Related
If you run the following code:
SByte w = -5;
Console.WriteLine(w.CompareTo(0));
Int32 x = -5;
Console.WriteLine(x.CompareTo(0));
SByte y = 5;
Console.WriteLine(y.CompareTo(0));
Int32 z = 5;
Console.WriteLine(z.CompareTo(0));
then you get the following output:
-5
-1
5
1
Why do these methods with the same name that have almost identical descriptions in the MSDN documentation behave so differently?
Because the SByte.CompareTo() is implemented like
return m_value - value;
so a simple subtraction. This works because the m_value is converted automatically to int, and any possibile combination of values is "legal" with int.
With two Int32 this can't be done, because for example Int32.MinValue.CompareTo(Int32.MaxValue) would become Int32.MinValue - Int32.MaxValue that would be outside the int range, and in fact it is implemented as two comparisons:
if (m_value < value) return -1;
if (m_value > value) return 1;
return 0;
in general
The only important "thing" of the returned value of a CompareTo is its sign (or if it is 0). The "value" is irrelevant. The return value of 1, 5, 500, 5000, 5000000 of CompareTo() are the same. CompareTo can't be used to measure "distance" between numbers. So both implementations are equivalent.
It is totally wrong to do:
if (someValue.CompareTo(someOtherValue) == -1)
you must always
if (someValue.CompareTo(someOtherValue) < 0)
why the SByte.CompareTo is built that way
SByte.CompareTo is implementing a "branchless" comparison (there are no ifs in the code, the flow of code is linear). Processors have problems with branches, so branchless code could be faster than "branchful" code, so this microoptimization. Clearly SByte.CompareTo could have been written as Int32.CompareTo.
why any negative value is equivalent to -1 (and any positive value is equivalent to +1)
This is probably something that is derived directly from the C language: the qsort function for example to compare items uses a user-defined method that is like:
Pointer to a function that compares two elements.
This function is called repeatedly by qsort to compare two elements. It shall follow the following prototype:
int compar (const void* p1, const void* p2);
Taking two pointers as arguments (both converted to const void*). The function defines the order of the elements by returning (in a stable and transitive manner):
return value meaning
<0 The element pointed to by p1 goes before the element pointed to by p2
0 The element pointed to by p1 is equivalent to the element pointed to by p2
>0 The element pointed to by p1 goes after the element pointed to by p2
how is the .CompareTo implemented in other primitive types?
SByte, Byte, Int16, UInt16, Char all use the subtraction "method", while Int32, UInt32, Int64, UInt64 all use the if "method".
Looking at the source for these two methods, they are implemented differently:
public int CompareTo(sbyte value)
{
return (int)(this - value);
}
vs
public int CompareTo(int value)
{
if (this < value)
{
return -1;
}
if (this > value)
{
return 1;
}
return 0;
}
But none of this matters, since the sign of the returned value is the only thing that you should be checking.
I'm new to C#, coming from Java, and I'd like to check whether a certain object is a Number (it can be an Integer, Double, Float, etc). In Java I did this by saying if (toRet instanceof Number). I'm hoping there's a similar C# thing like if (toRet is Number) but thus far I haven't been able to find a Number class that does this. Is there a way to do this, or do I have to manually check for Integer, Double, etc?
Edit for more info: Basically what I want to do is eventually have a byte array. However, when the array is stored in a text file, the parser I'm using can sometimes think it's an integer array or a double array. In Java, I had this:
JSONArray dblist = (JSONArray)result_;
byte[] reallyToRet = new byte[dblist.size()];
Object toComp = dblist.get(0);
if (toComp instanceof Number)
for (int i=0; i < dblist.size(); ++i) {
byte elem = ((Number) dblist.get(i)).byteValue();
reallyToRet[i] = elem;
}
return reallyToRet;
}
The important bit here is the if statement. Sometimes the objects in dblist would parse as integers, sometimes as doubles, and only rarely as bytes, but all I really care about at the end is the byte value.
Well, yeah, but it's an extension method that just ORs all the possibilities.
This is it:
public static bool IsNumber(this object value)
{
return value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal
|| value is BigInteger;
}
and you would use it like this:
if (toRet.IsNumber());
This needs to be in a static class.
I am not sure about any class for that. But you can check for instance, for integer see
int val;
if(Int32.TryParse(integer, out val))
else
Unlikely, you can use Double.TryParse(number, out val) etc.
In C#, if we define an enum that contains a member correspondingto a negative value, and then we iterate over that enum's values, the negative value does not come first, but last. Why does that happen? In other languages (C, C++, Ada, etc), iterating over an enum will give you the order in which you defined it.
MSDN has a good example of this behavior:
using System;
enum SignMagnitude { Negative = -1, Zero = 0, Positive = 1 };
public class Example
{
public static void Main()
{
foreach (var value in Enum.GetValues(typeof(SignMagnitude)))
{
Console.WriteLine("{0,3} 0x{0:X8} {1}",
(int) value, ((SignMagnitude) value));
}
}
}
// The example displays the following output:
// 0 0x00000000 Zero
// 1 0x00000001 Positive
// -1 0xFFFFFFFF Negative
From the very documentation page you link to, my emphasis:
The elements of the array are sorted by the binary values of the enumeration constants (that is, by their unsigned magnitude).
Digging into the CLR code (the 2.0 SSCLI) and getting far lower-level than I'm really comfortable with, it looks like ultimately this is because internally enum values are stored in something that looks like this (note this is C++):
class EnumEEClass : public EEClass
{
friend class EEClass;
private:
DWORD m_countPlusOne; // biased by 1 so zero can be used as uninit flag
union
{
void *m_values;
BYTE *m_byteValues;
USHORT *m_shortValues;
UINT *m_intValues;
UINT64 *m_longValues;
};
LPCUTF8 *m_names;
As can be seen, it's unsigned types that hold the actual values - so when these values are emitted for enumeration, naturally they are in their unsigned order.
Is the use of implicit enum fields to represent numeric values a necessarily bad practice?
Here is a use case: I want an easy way to represent hex digits, and since C# enums are based on integers, they seem like a natural match. I don't like a char or a string here, because I have to explicitly validate their values. The problem with enums is that digits [0-9] are not valid field identifiers (with good reason). It occurred to me that I don't need to declare the digits 0-9, because they are implicitly present.
So, my hex digit enum would look like:
public enum Hex : int {
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15
}
So, I could write Tuple<Hex,Hex> r = Tuple.Create(Hex.F,(Hex)1);, and r.Item1.ToString() + r.Item2.ToString() would give me "F1". Basically, my question is that if the ToString() value of the numeric constant is what I want to name the enum field, why is it problematic to omit the declaration entirely?
An alternative representation as an enum could have the fields declared with some prefix, such as:
public enum Hex : int {
_0 = 0,
_1 = 1,
_2 = 2,
_3 = 3,
_4 = 4,
_5 = 5,
_6 = 6,
_7 = 7,
_8 = 8,
_9 = 9,
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15
}
The problem is that the above example would give me "F_1" instead of "F1". Obviously, this is easy to fix. I'm wondering if there are additional problems with the implicit approach that I am not considering.
It's bad practice because it's a clever trick that's surprising to the people who read your code. It surprised me that it actually worked, it had me saying wtf. Remember the only valid measurement of code quality:
Clever tricks don't belong in code that's meant to be read and maintained by others. If you want to output a number as hex, convert it to a hex string using the normal String.Format("{0:X}", value)
This is a fundamentally broken way to handle hex. Hex is a human interface detail. It is always a string, a representation of a number. Like "1234" is a representation of the value 1234. It happens to be "4D2" when represented in hex but the number in your program is still 1234. A program should only ever concern itself with the number, never with the representation.
Converting a number to hex should only happen when you display the number to human eyes. Simple to do with ToString("X"). And to parse back from human input with TryParse() using NumberStyles.HexNumber. Input and output, at no other point should you ever deal with hex.
I would define a struct for HexDigit. You can add HexDigit 'A' to 'F' as static constants (or static readonly fields).
You can define implicit converters to allow conversion of integers 0-9, conversion to integers, and you can override ToString() to make you Tuples look nice.
That will be much more flexible than an enum.
In my opinion, it is a bad practice. If you need Hex representation, simply create a helper class that handles all the operations you require.
As this article suggests, these code snippets will help in creating the helpers:
// Store integer 182
int decValue = 182;
// Convert integer 182 as a hex in a string variable
string hexValue = decValue.ToString("X");
// Convert the hex string back to the number
int decAgain = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
The reason I believe it's bad practice is because it's not object oriented, and it runs into the problem of relying on the enum to translate all the values hard-coded - which is bad. If you can avoid hard coding anything, it's always a step in the right direction. Also, a helper class is extensible and can be improved over time for additional functionality.
That being said, I DO like the simplicity of enums, but, again, that doesn't supersede the need to keep things OO in my opinion.
I'm not sure what you're actually trying to accomplish here, but if you're looking to limit something to two hexadecimal digits, why wouldn't you just declare it as a byte? While your enum hack is clever, I don't actually see the need for it. It's also likely to be misundertstood if passed to another programmer without explanation as your use of undeclared values against your enum is counterintuitive.
Regarding number bases and literal representations, an integer in computing isn't base-10 or base-16 natively, it's actually base-2 (binary) under the covers and any other represenations are a human convenience. The language already contains ways to represent literal numbers in both decimal and hexadecimal format. Limiting the number is a function of appropriately choosing the type.
If you are instead trying to limit something to any arbitrary even quantity of hexadecimal digits, perhaps simply initializing a byte array like this would be more appropriate:
byte[] hexBytes = new byte[3] { 0xA1, 0xB2, 0xC3 };
Also, by keeping your value as a regular numeric type or using a byte array rather than putting it into Tuples with enums, you retain simple access to a whole range of operations that otherwise become more difficult.
Regarding limiting your numbers to arbitrary odd quantities of hexadecimal digits, you can choose a type that contains at least your desired value + 1 digit and constrain the value at runtime. One possible implementation of this is as follows:
public class ThreeNibbleNumber
{
private _value;
public ushort Value
{
get
{
return _value;
}
set
{
if (value > 4095)
{
throw new ArgumentException("The number is too large.");
}
else
{
_value = value;
}
}
}
public override string ToString()
{
return Value.ToString("x");
}
}
In one of your comments on another answer, you reference the idea of doing CSS colors. If that's what you desire a solution like this seems appropriate:
public struct CssColor
{
public CssColor(uint colorValue)
{
byte[] colorBytes = BitConverter.GetBytes(colorValue);
if (BitConverter.IsLittleEndian)
{
if (colorBytes[3] > 0)
{
throw new ArgumentException("The value is outside the range for a CSS Color.", "s");
}
R = colorBytes[2];
G = colorBytes[1];
B = colorBytes[0];
}
else
{
if (colorBytes[0] > 0)
{
throw new ArgumentException("The value is outside the range for a CSS Color.", "s");
}
R = colorBytes[1];
G = colorBytes[2];
B = colorBytes[3];
}
}
public byte R;
public byte G;
public byte B;
public override string ToString()
{
return string.Format("#{0:x}{1:x}{2:x}", R, G, B).ToUpperInvariant();
}
public static CssColor Parse(string s)
{
if (s == null)
{
throw new ArgumentNullException("s");
}
s = s.Trim();
if (!s.StartsWith("#") || s.Length > 7)
{
throw new FormatException("The input is not a valid CSS color string.");
}
s = s.Substring(1, s.Length - 1);
uint color = uint.Parse(s, System.Globalization.NumberStyles.HexNumber);
return new CssColor(color);
}
}
I don't particularly see why you would want to do this, but you could use the Description attribute on each of your enum values to get rid of the _ and create some kind of static function that allows you to get one of your enum values easily like Hex(15) -> 'F'.
public enum Hex {
[Description("0")] _0 = 0,
...
}
Expanding on Jon Skeet's answer to This Previous Question. Skeet doesn't address the failure that occurs when negative values and two's complement values enter the picture.
In short, I want to convert any simple type (held in an unknown boxed object) to System.UInt64 so I can work with the underlying binary representation.
Why do I want to do this? See the explanation at the bottom.
The example below shows the cases where Convert.ToInt64(object) and Convert.ToUInt64(object) both break (OverflowException).
There are only two causes for the OverflowExceptions below:
-10UL causes an exception when converting to Int64 because the negative value casts to 0xfffffffffffffff6 (in the unchecked context), which is a positive number larger than Int64.MaxValue. I want this to convert to -10L.
When converting to UInt64, signed types holding negative values cause an exception because -10 is less than UInt64.MinValue. I want these to convert to their true two's complement value (which is 0xffffffffffffffff6). Unsigned types don't truly hold the negative value -10 because it is converted to two's complement in the unchecked context; thus, no exception occurs with unsigned types.
The kludge solution would seem to be conversion to Int64 followed by an unchecked cast to UInt64. This intermediate cast would be easier because only one instance causes an exception for Int64 versus eight failures when converting directly to UInt64.
Note: The example uses an unchecked context only for the purpose of forcing negative values into unsigned types during boxing (which creates a positive two's complement equivalent value). This unchecked context is not a part of the problem at hand.
using System;
enum DumbEnum { Negative = -10, Positive = 10 };
class Test
{
static void Main()
{
unchecked
{
Check((sbyte)10);
Check((byte)10);
Check((short)10);
Check((ushort)10);
Check((int)10);
Check((uint)10);
Check((long)10);
Check((ulong)10);
Check((char)'\u000a');
Check((float)10.1);
Check((double)10.1);
Check((bool)true);
Check((decimal)10);
Check((DumbEnum)DumbEnum.Positive);
Check((sbyte)-10);
Check((byte)-10);
Check((short)-10);
Check((ushort)-10);
Check((int)-10);
Check((uint)-10);
Check((long)-10);
//Check((ulong)-10); // OverflowException
Check((float)-10);
Check((double)-10);
Check((bool)false);
Check((decimal)-10);
Check((DumbEnum)DumbEnum.Negative);
CheckU((sbyte)10);
CheckU((byte)10);
CheckU((short)10);
CheckU((ushort)10);
CheckU((int)10);
CheckU((uint)10);
CheckU((long)10);
CheckU((ulong)10);
CheckU((char)'\u000a');
CheckU((float)10.1);
CheckU((double)10.1);
CheckU((bool)true);
CheckU((decimal)10);
CheckU((DumbEnum)DumbEnum.Positive);
//CheckU((sbyte)-10); // OverflowException
CheckU((byte)-10);
//CheckU((short)-10); // OverflowException
CheckU((ushort)-10);
//CheckU((int)-10); // OverflowException
CheckU((uint)-10);
//CheckU((long)-10); // OverflowException
CheckU((ulong)-10);
//CheckU((float)-10.1); // OverflowException
//CheckU((double)-10.1); // OverflowException
CheckU((bool)false);
//CheckU((decimal)-10); // OverflowException
//CheckU((DumbEnum)DumbEnum.Negative); // OverflowException
}
}
static void Check(object o)
{
Console.WriteLine("Type {0} converted to Int64: {1}",
o.GetType().Name, Convert.ToInt64(o));
}
static void CheckU(object o)
{
Console.WriteLine("Type {0} converted to UInt64: {1}",
o.GetType().Name, Convert.ToUInt64(o));
}
}
WHY?
Why do I want to be able to convert all these value types to and from UInt64? Because I have written a class library that converts structs or classes to bit fields packed into a single UInt64 value.
Example: Consider the DiffServ field in every IP packet header, which is composed of a number of binary bit fields:
Using my class library, I can create a struct to represent the DiffServ field. I created a BitFieldAttribute which indicates which bits belong where in the binary representation:
struct DiffServ : IBitField
{
[BitField(3,0)]
public PrecedenceLevel Precedence;
[BitField(1,3)]
public bool Delay;
[BitField(1,4)]
public bool Throughput;
[BitField(1,5)]
public bool Reliability;
[BitField(1,6)]
public bool MonetaryCost;
}
enum PrecedenceLevel
{
Routine, Priority, Immediate, Flash, FlashOverride, CriticEcp,
InternetworkControl, NetworkControl
}
My class library can then convert an instance of this struct to and from its proper binary representation:
// Create an arbitrary DiffServe instance.
DiffServ ds = new DiffServ();
ds.Precedence = PrecedenceLevel.Immediate;
ds.Throughput = true;
ds.Reliability = true;
// Convert struct to value.
long dsValue = ds.Pack();
// Create struct from value.
DiffServ ds2 = Unpack<DiffServ>(0x66);
To accomplish this, my class library looks for fields/properties decorated with the BitFieldAttribute. Getting and setting members retrieves an object containing the boxed value type (int, bool, enum, etc.) Therefore, I need to unbox any value type and convert it to it's bare-bones binary representation so that the bits can be extracted and packed into a UInt64 value.
I'm going to post my best solution as fodder for the masses.
These conversions eliminate all exceptions (except for very large float, double, decimal values which do not fit in 64-bit integers) when unboxing an unknown simple value type held in object o:
long l = o is ulong ? (long)(ulong)o : Convert.ToInt64(o));
ulong u = o is ulong ? (ulong)o : (ulong)Convert.ToInt64(o));
Any improvements to this will be welcomed.