【问题标题】:Double-casting required to convert from int as object to float从 int 作为对象转换为 float 需要双重转换
【发布时间】:2013-07-05 05:01:46
【问题描述】:

这是我遇到的一个奇怪的问题。我有一个返回object 的函数。在特定情况下,我确定该对象是int,但另一个调用此函数的函数需要float。我可以让它工作的唯一方法是:

private object parser(string expr) {...}

private float parseFloat(string expr)
{
    ...
    object result = parser(expr);
    if (result is int)
        return (float)(int)result;
    else
        return (float)result;
}

没有那种类型检查,我根本无法让它工作,即使我知道在这种情况下解析器函数的结果是int。 (你可以在调试器中看到它。)我更喜欢这条线:

private float parseFloat(string expr)
{
    ...
    return (float)parser(expr);
}

(类型检查是事先完成的,并且永远不应该使用不会评估为floatint 的表达式调用parseFloat。)有什么理由需要双重转换这个变量吗?我显然不想在所有情况下都重复它,因为如果来自parser float 的返回,它会先将它截断为int 而我们不会想要那个。 (是的,我尝试将floatint 替换为SingleInt32 等。没有任何区别。)

我看到了this question,但这取决于提前知道类型,它提供的唯一解决方案是这种双重转换技巧,在我的情况下,除非我先进行类型检查,否则它会截断floats ,这也需要一个额外的局部变量来保存结果。有什么办法可以避免这个额外的步骤?

【问题讨论】:

  • 如果您确定它是一个,为什么不提供ParseFloat
  • 这就是 ... 的用途。传递给 ParseFloat 的表达式要么是浮点字面量(在 ... 代码中处理),要么是返回浮点数的函数。 parser 的工作就是解释这些函数(实际上可能是几个嵌套函数)。
  • 这是一个常见问题。我在 2009 年回答了这个问题,在这里:ericlippert.com/2009/03/03/representation-and-identity
  • @EricLippert 不错的文章,解释得很好。并没有让它变得不那么烦人,但至少有一个很好的理由说明它不能按您期望的方式工作。

标签: c# object casting boxing


【解决方案1】:

当您对已放入 object 框中的值类型进行拆箱时,您必须使用正确的强制转换。从一种数字类型到另一种数字类型的转换是拆箱后的额外转换。

然而,在你的情况下,第二个演员真的是隐含的,所以你可以稍微简化一下:

if (result is int)
    return (int)result;
else
    return (float)result;

为什么一开始就有装箱的值类型?如果您不知道装箱的是什么类型,要取消装箱,您通常必须按照您的方式与is 核对。

但是请注意,对于像这些内置数字类型这样的 (IConvertible) 类型,您还可以使用 Convert 类,因此:

return Convert.ToSingle(result);

它看起来更漂亮,但我不认为它更快。它可能会做同样的类型检查。

如果parser 只提供数字类型,并且由于某种原因您坚持将这些值类型装箱,那么您不妨将它们装箱到IConvertible,这样就涉及到像这样更改返回类型:

private IConvertible parser(string expr) {...}

那么你的代码可以这样:

IConvertible result = parser(expr);  // it's some number boxed in an IConvertible reference type
return result.ToSingle(null);  // instance method in IConvertible

【讨论】:

  • 一项改进——尽管仍然需要一个临时变量和一个类型检查。真的没有办法解决吗?我能想到的唯一另一件事是将其减少到?: 而不是if\ else,但仍然没有节省额外的步骤。
  • 好的,您编辑中的Convert.Tosingle 技巧确实解决了问题,尽管目前很难在两者之间进行性能比较。似乎常规铸造应该足够聪明,可以根据需要对结果进行拆箱和重新装箱?无论如何,至少对于内置类型。
  • 关于您上次的编辑 - 不,parser 可以返回相当多的非数字类型,这就是它返回 object 的原因。它还处理bools、strings、自定义 Vector4 类和以上所有内容的Lists。一些脚本函数甚至有一个void 返回(虽然在后台它只是返回null)。每种类型都有自己的Parse 函数,如上面的ParseFloat。这里代码太多了,我只是简化了它以显示问题。
  • @DarrelHoffman 好吧,这就是 C# 规则的制定方式,我认为这是有充分理由的。从intfloat 的转换是表示的二进制变化(一个是二进制补码整数表示,另一个是IEEE 浮点表示)。 C# 编译器将不知道它是否必须同时发出拆箱转换和数字转换。我看到的唯一其他选择(除了我的回答中的想法)是使用dynamic。如果rdynamic 类型,则表达式(float)r 将起作用,直到运行时才决定隐含的转换。
  • 谢谢 - 我选择了 return Convert.ToSingle(parser(expr)); 这避免了创建临时变量和类型检查,即使 Convert 类在后台做同样的事情。 dynamic 解决方案似乎也有效,但它是一个额外的行,因为必须创建一个变量。不确定哪个性能更好,我可能需要进行更大规模的测试才能看到任何差异。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-13
相关资源
最近更新 更多