Reputation: 105
Using System.Data.SqlClient & ADO.
How come this code with casting works fine:
foreach (DataRow row in dsUtilities.Tables[0].Rows)
{
o = row[i + 2];
d = (double)o;
f = (float)d;
u.CostMonth[i] = f; ...
But this code with casting crashes:
foreach (DataRow row in dsUtilities.Tables[0].Rows)
{
u.CostMonth[i] = (float)row[i+2];
"Specified cast is not valid"
For context, below is the complete routine.... I can do it the long way as a workaround, but I want to know if I'm doing something wrong, or it this a plainly a oddity of the language?
private void LoadUtilities()
{
/* Normally this should only be called once, and then keep the dataset current, and update db separately... */
string CS = ConfigurationManager.ConnectionStrings["DbConn"].ToString();
dsUtilities = null;
lstUtilities = null;
lstUtilities = new List<clsUtility>();
this.dtpLastUtilityRefreshDate.Value = DateTimePicker.MinimumDateTime;
lblAsterisk.Visible = false;
txtWarning.Visible = false;
using (SqlConnection con = new SqlConnection(CS))
{
SqlDataAdapter da = new SqlDataAdapter("GetUtilities", con); // Using a Store Procedure.
//SqlDataAdapter da = new SqlDataAdapter("SELECT 'this is a test text' as test", con); To use a hard coded query.
da.SelectCommand.CommandType = CommandType.StoredProcedure;
dsUtilities = new DataSet();
//da.SelectCommand.Parameters.AddWithValue("@ggg", 95); // Repeat for each parameter present in the Store Procedure.
da.Fill(dsUtilities); // Fill the dataset with the query data
foreach (DataRow row in dsUtilities.Tables[0].Rows)
{
clsUtility u;
u = new clsUtility();
u.UtilityId = (int)row[0];
u.Utility = (string)row[1];
if (row[2] is System.DBNull)
{
u.UNC_SourcePath = @"C:\";
}
else u.UNC_SourcePath = (string)row[2];
int i;
i = 1;
object o;
double d;
float f;
while (i < 13)
{
if (row[i+2] is System.DBNull) { } else
{
o = row[i + 2];
d = (double)o;
f = (float)d;
u.CostMonth[i] = f;
u.CostMonth[i] = (float)row[i+2];
}
i++;
}
if (row[16] is System.DBNull) { } else { u.LineLossPct = (float)row[16]; }
if (row[17] is System.DBNull) { } else { u.GRT = (float)row[17]; }
if (row[18] is System.DBNull) { } else { u.POR = (float)row[18]; }
if (row[19] is System.DBNull) { } else { u._12MonthCaps = (float)row[19]; }
if (row[20] is System.DBNull) { } else { u._12MonthNits = (float)row[20]; }
if (row[21] is System.DBNull) { } else { u._12MonthRate = (float)row[21]; }
if (row[22] is System.DBNull) { } else { u.NonPolarCTA = (float)row[22]; }
if (row[23] is System.DBNull) { } else { u.PolarCTA = (float)row[23]; }
if (row[24] is System.DBNull)
{
u.LastUploadedByTitan = DateTimePicker.MinimumDateTime;
}
else
{
u.LastUploadedByTitan = (DateTime)row[24];
}
if (row[25] is System.DBNull)
{
u.LastRefreshedByUtility = DateTimePicker.MinimumDateTime;
}
else
{
u.LastRefreshedByUtility = (DateTime)row[25];
}
this.lstUtilities.Add(u);
u = null;
}
this.cboUtility.DataSource = lstUtilities;
this.cboUtility.DisplayMember = "Utility";
this.cboUtility.ValueMember= "UtilityId";
this.cboUtility.SelectedIndex = 0;
GetDateStamps();
}
}
da.Fill(dsUtilities); // Fill the dataset with the query data
foreach (DataRow row in dsUtilities.Tables[0].Rows)
{
clsUtility u;
u = new clsUtility();
u.UtilityId = (int)row[0];
u.Utility = (string)row[1];
if (row[2] is System.DBNull)
{
u.UNC_SourcePath = @"C:\";
}
else u.UNC_SourcePath = (string)row[2];
int i;
i = 1;
object o;
double d;
float f;
while (i < 13)
{
if (row[i+2] is System.DBNull) { } else
{
o = row[i + 2];
d = (double)o;
f = (float)d;
u.CostMonth[i] = f;
u.CostMonth[i] = (float)row[i+2];
}
i++;
} ….
Upvotes: 1
Views: 81
Reputation: 42245
The confusion stems from the fact that the syntax (T)x
(where T
is a type and x
is a variable) can actually mean different things, depending on context.
The C# language defines a number of conversions between numeric types. Some of these are explicit conversions which require the cast syntax, e.g.:
double d = 3.0;
float f = (float)d;
Built-in numeric conversions compile to appropriate IL instructions depending on the types convolved, e.g. the conv
family.
Boxing occurs when a value type is assigned to a variable of type object
(or another reference type), and results in the value type being stored in a box on the heap. Unboxing is used to get the value type back out of the box. The cast syntax is used here as well, but the type used in the cast must match the type of the boxed object.
object box = 3.0;
double d = (double)box; // OK: the box contains a double
float f = (float)box; // Error: the box contains a double, not a float
Unboxing compiles to the unbox
IL instruction.
Types in C# can define conversions to or from other types. Such conversions can be explicit, in which case the cast syntax is required to invoke them.
For example, the Decimal
type has the following defined:
public static explicit operator float(Decimal value) {
return ToSingle(value);
}
This would be invoked by e.g.:
decimal m = 3.0m;
float f = (float)m;
The operator defined above is compiled to a static method called op_Explicit(decimal m)
, and the cast syntax compiles to a call to this method.
There are other types of explicit conversions (reference, dynamic, nullable) which aren't relevant here, but also use the cast syntax.
With this out of the way, your code is actually doing an unbox followed by a built-in numeric conversion:
o = row[i + 2];
d = (double)o; // Unbox
f = (float)d; // Built-in numeric conversion
You can't write float f = (float)o
, because that's interpreted as trying to unbox o
as a float
, when actually it contains a boxed double
.
You need to have two steps, the first to unbox, and the second to convert to a float
.
You can of course write it on a single line, as:
float f = (float)(double)row[i + 2];
Upvotes: 4