英文:
c# casting oddity -- any explanations?
问题
这段代码中的问题在于数据类型转换。第一段代码中,您使用了中间变量 o
、d
和 f
,首先将 row[i + 2]
转换为 double
类型,然后再将其转换为 float
类型。这种逐步的转换可能导致了数据类型匹配,因此没有出现问题。
而在第二段代码中,您直接将 row[i+2]
转换为 float
,这可能会导致数据类型不匹配,从而出现 "Specified cast is not valid" 错误。
要解决这个问题,您可以尝试在第二段代码中使用与第一段相同的中间变量来进行逐步转换,或者确保 row[i+2]
的数据类型与 float
兼容。
英文:
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++;
} ….
答案1
得分: 4
以下是翻译好的部分:
混淆源于语法 (T)x
(其中 T
是类型,x
是变量),实际上可以根据上下文意义不同。
内置数值转换
C# 语言定义了许多数值类型之间的转换。其中一些是需要使用强制转换语法的显式转换,例如:
double d = 3.0;
float f = (float)d;
内置数值转换会根据涉及的类型编译成相应的 IL 指令,例如 conv
系列。
拆箱
当将值类型分配给 object
类型(或其他引用类型)的变量时,会发生装箱,导致值类型存储在堆上的盒子中。拆箱用于将值类型从盒子中取出。这里也使用了强制转换语法,但强制转换中使用的类型必须与盒子中的对象类型匹配。
object box = 3.0;
double d = (double)box; // OK:盒子中包含 double
float f = (float)box; // Error:盒子中包含 double,而不是 float
拆箱编译成 unbox
IL 指令。
用户定义的类型转换
在 C# 中,类型可以定义与其他类型之间的转换。这些转换可以是显式的,如果是这种情况,就需要使用强制转换语法来调用它们。
例如,Decimal
类型有以下定义(https://referencesource.microsoft.com/#mscorlib/system/decimal.cs,1110):
public static explicit operator float(Decimal value) {
return ToSingle(value);
}
可以通过以下方式调用上面定义的运算符:
decimal m = 3.0m;
float f = (float)m;
上面定义的运算符被编译成名为 op_Explicit(decimal m)
的静态方法,而强制转换语法被编译成对此方法的调用。
其他
还有其他类型的显式转换(引用类型、动态类型、可空类型),这里不相关,但也使用强制转换语法。
搞清楚了这些,你的代码实际上执行了拆箱,然后进行内置数值转换:
o = row[i + 2];
d = (double)o; // 拆箱
f = (float)d; // 内置数值转换
你不能写成 float f = (float)o
,因为那会被解释为尝试将 o
视为 float
进行拆箱,实际上它包含的是装箱的 double
。
你需要分为两个步骤,第一个步骤是拆箱,第二个步骤是转换为 float
。
当然,你也可以在一行中写成:
float f = (float)(double)row[i + 2];
英文:
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.
Built-in numeric conversions
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.
Unboxing
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.
User-defined type conversions
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.
Others
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];
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论