We have code that casts an object to a short.
type.BusinessAreaID = (short)caType.credit_assessment_biz_areaReference.EntityKey.EntityKeyValues[0].Value;
The object has the value of 2.
This code (unit test) works on the PC of the developer that created the code. But we get a invalid cast exception on the build server and on another dev PC.
The BusinessAreaID is of type short.
The Dll is also in the GAC, we have updated that so that it should be the same.
Anyone have any ideas.
You can't unbox a value to a different type. For example, this works:
short x = 2;
object y = (object)x;
short z = (short)y;
but this does not:
int x = 2;
object y = (object)x;
short z = (short)y; // InvalidCastException
Are you sure the value stored in caType...lues[0].Value is of type short?
Related
I'm preparing for an exam and stumbled over a question regarding boxing/unboxing.
I always thought if i box let's say a float like this:
float x = 4.5f;
object o = x;
If a want a value type variable back from o, I will have to unbox it to a float.
float y = (float)o;
This should throw an exception:
int z = int(o);
If I want to cast the value stored in o to an int I will have to unbox it first and cast afterwards like this:
int z = (int)(float)o;
Now the question i stumbled upon:
Suppose you have a method like this:
public static void FloorTemperature(float degrees) {
object degreesRef = degrees;
`xxx`
Console.WriteLine(result);
}
You need to ensure the application does not throw exceptions on invalid conversions. Which code segment should you insert for xxx (I think invalid conversion are invalid cast exceptions):
(a) int result = (int)degreesRef;
(b) int result = (int)(float)degreesRef;
The correct solution is (a), but to me (b) looks correct. So can you please enlighten me? What am I missing?
Kind regards
You aren't missing anything
The answer should be (b) because:
(a) throws an exception since you are trying to cast object to int.
(b) is correct since you first cast it to float then you cast it to int which rounds it but doesn't throw an exception.
I'm new to C# (but I know C++, JavaScript and Java) and I'm using JSON.net to parse a JSON file to a Dictionary<string, object>. Now I'm trying to store a value which was parsed as a long into a double variable. (I looked at the inspector and it says the dictionary value is of type object{long}.)
object obj = 123L;
double dbl = (double)obj;
Which gives me an InvalidCastException. I experimented a bit and found out that Convert.ToDouble(obj) works out just fine. I looked around and couldn't find anything about that difference between casting and Convert-ing. Also:
var v = 123L;
double dbl = (double)v;
This works perfectly. I guess that's because in this case, var is turned into Int64 at compile time.
Because that's an unboxing conversion. In order to cast a boxed long to a double, you can first unbox it, then actually convert it, for example: (not tested)
object obj = 123L;
double dbl = (double)(long)obj;
The second snippet works for the reason you identified.
When you cast an object to a value type, you unbox it. Unboxing is only possible to the type of the value type. As the type of the object is long, you can't unbox it to double. Check the Convert class:
double dbl = Convert.ToDouble(obj, null);
i use the following code to map data from sql reader to C# Objects. the Power is of data type float in both code and sql database .. y is the cast error message occuring?
private Entity.PanelDetail MapDataReader(SqlDataReader dataReader)
{
Entity.PanelDetail panelDetail = new Entity.PanelDetail();
panelDetail.IdPanelDetail = DataReaderExtensions.GetStringOrNull(dataReader, "idPanelDetail");
panelDetail.IdDeviceDetail = DataReaderExtensions.GetStringOrNull(dataReader, "idDeviceDetail");
panelDetail.Power = DataReaderExtensions.GetFloatOrNull(dataReader, "Power");
panelDetail.Current = DataReaderExtensions.GetFloatOrNull(dataReader, "Current");
panelDetail.Length = DataReaderExtensions.GetFloatOrNull(dataReader, "Length");
panelDetail.Width = DataReaderExtensions.GetFloatOrNull(dataReader, "Width");
panelDetail.CreatedBy = DataReaderExtensions.GetStringOrNull(dataReader, "CreatedBy");
panelDetail.CreationDate = DataReaderExtensions.GetDateTimeOrNull(dataReader, "CreationDate");
panelDetail.ModifiedBy = DataReaderExtensions.GetStringOrNull(dataReader, "ModifiedBy");
panelDetail.ModifiedDate = DataReaderExtensions.GetDateTimeOrNull(dataReader, "ModifiedDate");
panelDetail.IsActive = DataReaderExtensions.GetBoolOrNull(dataReader, "IsActive");
panelDetail.IsDeleted = DataReaderExtensions.GetBoolOrNull(dataReader, "IsDeleted");
return panelDetail;
}
My guess that the value is being returned as a boxed double instead of float. Try to use
(float) dataReader.GetDouble(fieldOrdinal);
See Mapping CLR Parameter Data
I would make a good bet that the data type being return is actually a (boxed) double.
Update: Actually I just found that an SQL float maps to a .NET double on MSDN (so yes this is your problem): http://msdn.microsoft.com/en-us/library/ms131092.aspx
Try this as a test:
(float)dataReader.GetDouble(fieldOrdinal);
The reason is that SQL Float is double in .net. You can see the complete mapping here. Therefore, as others have suggested you need to read double and then try to cast it to float.
(float)dataReader.GetDouble("Power");
struct Point
{
public int x;
public int y;
}
void Main()
{
Point p;
p.x = 1;
p.y = 1;
Object o = p;
((Point) o).x = 4; // error
((Point) o).x = 5; // error
((Point) o).x = 6; // error
p = (Point) o // expect 6
}
Why doesn't it compile to
ldloc.1 // o
unbox Point
ldc.i4.4
stfld Point.x
Where C++ CLI allows it.
For those who don't know, unbox is not required to create a copy of value types, instead it pushes a pointer to the value on to the stack.
Only assignment would create a copy.
Because of how value types work, the boxed Point is a copy of the original, and "unboxing" it by casting back to Point creates yet another copy. From the C# language spec (§1.3, "Types and Variables"):
When a value of a value type is converted to type object, an object instance, also called a “box,” is allocated to hold the value, and the value is copied into that box. Conversely, when an object reference is cast to a value type, a check is made that the referenced object is a box of the correct value type, and, if the check succeeds, the value in the box is copied out.
Modifying the copy wouldn't change the original anyway, so it wouldn't make much sense to allow it.
As for C++...well...of course, the rules of C# don't necessarily apply to it. :) The CLR actually has quite a bit more flexibility with pointers and references than you'd first think, and C++ -- being known for such flexibility -- probably takes advantage of it.
You can't do this, because the result of unboxing is a copy of the boxed value, not the boxed value itself. And casting object to a value type is the definition of unboxing. So, if the compiler allowed you to do this, it would be very confusing, because the assignments wouldn't actually do anything.
I think the reason your code works in C++/CLI is because that language in general has more support for working (or not) with references, including strongly-typed boxes (e.g. Point^) and treating (some) classes as value types (e.g. using MemoryStream without ^).
You can accomplish this using the System.Runtime.CompilerService.Unsafe.Unbox function:
static void Main()
{
Point p;
p.x = 1;
p.y = 1;
Object o = p;
Unsafe.Unbox<Point>(o).x = 6; // error
p = (Point)o; // 6
Console.WriteLine(p.x);
}
As the documentation notes, and I presume the reason it is considered unsafe, is that you must not do this with an immutable built-in type [e.g. Unbox<int>(i) = 42], and the system does nothing to enforce this.
The below code fails at the last assignment:
static void Main(string[] args)
{
int a = 5;
object b = 5;
System.Diagnostics.Debug.Assert( a is int && b is int );
double x = (double)a;
double y = (double)b;
}
If both a and b are int, what is the cause of this error?
This is an extremely frequently asked question. See https://ericlippert.com/2009/03/03/representation-and-identity/ for an explanation.
Snippet:
I get a fair number of questions about the C# cast operator. The most frequent question I get is:
short sss = 123;
object ooo = sss; // Box the short.
int iii = (int) sss; // Perfectly legal.
int jjj = (int) (short) ooo; // Perfectly legal
int kkk = (int) ooo; // Invalid cast exception?! Why?
Why? Because a boxed T can only be unboxed to T. (*) Once it is unboxed, it’s just a value that can be cast as usual, so the double cast works just fine.
(*) Or Nullable<T>.
Unboxing requires the exact type - you can do this instead:
double y = (double)(int)b;
This is one of the rare cases where System.Convert comes in handy. You can use System.Convet.ToDouble(obj) to knock it out if you don't know before hand that it will be int.
Implicit casting is a compile-time operation. It's not possible for b of type object.
a is an int, but b is a reference to an object that is an int - it is what is called a boxed int. They are two different things, hence the different behaviors.