使用转换运算符转换对象失败
Casting an object with a conversion operator fails
所以我有这个对象,比如 DoubleContainer。
public struct DoubleContainer
{
private readonly double _value;
private DoubleContainer(double value)
{
_value = value;
}
public static implicit operator double(DoubleContainer doubleContainer)
{
return doubleContainer._value;
}
public static DoubleContainer Create(double value)
{
return new DoubleContainer(value);
}
}
除了将它作为对象传递给函数的情况外,几乎在所有情况下都可以按预期进行转换。
如果我传入 DoubleContainer,以下代码会生成 InvalidCastException:
public double GetDouble(Object input)
{
return (double)input;
}
如果我使用动态,我可以让它工作:
public double GetDouble(Object input)
{
return (double)(dynamic)input;
}
我的这个解决方案的问题是 Visual Studio 将 (dynamic) 变灰,因为它应该是多余的,所以有人可能会删除它。另外我不知道代码库中是否还有其他地方可能会出现同样的问题。
我可以对 DoubleContainer 的实现做些什么来使我的第一个 GetDouble() 实现正常工作?我尝试添加另一个从 Object 到 DoubleContainer 的隐式转换运算符,但是 "user-defined conversions to or from a base class are not allowed"...
将对象转换为 double 时,编译器不知道它应该调用您现有的 operator double
,因此它不会插入对您的用户定义运算符 double 的任何调用。
当你在中间插入 dynamic
时,编译器会生成类似“如果这个引用有一个双精度运算符,调用它”这样的东西,所以它有效。
因此,只要将 System.Object
转换为双精度数,它实际上就无法工作,而@juharr 建议的代码稍作修改后就可以工作:
public double GetDouble(Object input)
{
if (input is DoubleContainer)
{
var dc = (DoubleContainer)input;
return (double)dc;
}
return (double)input;
}
编辑:根据@SergiyKlimkov 评论修改代码
您无法使其工作,因为您只能将已装箱的结构(这就是您使用 (double) input
所做的)拆箱为确切的底层类型,原因在此 article 中有最佳描述埃里克·利珀特着。因此,无论何时您执行 (double) someObject
- 只有对象实际上是 double
,而不是 int
,而不是 float
,而不是 DoubleContainer
,它才会起作用。如果您期望其他类型 - 您可以更好地使用 Convert.ToDouble
。为了使它与您的类型一起使用,您需要它来实现 IConvertible
:
public struct DoubleContainer : IConvertible
{
private readonly double _value;
private DoubleContainer(double value)
{
_value = value;
}
public static implicit operator double(DoubleContainer doubleContainer)
{
return doubleContainer._value;
}
public static DoubleContainer Create(double value)
{
return new DoubleContainer(value);
}
public double ToDouble(IFormatProvider provider) {
return _value;
}
public bool ToBoolean(IFormatProvider provider) {
// delegate to your double
return ((IConvertible) _value).ToBoolean(provider);
}
// ... rest is skipped ...
然后它将与
一起工作
public double GetDouble(Object input)
{
return Convert.ToDouble(input);
}
所以我有这个对象,比如 DoubleContainer。
public struct DoubleContainer
{
private readonly double _value;
private DoubleContainer(double value)
{
_value = value;
}
public static implicit operator double(DoubleContainer doubleContainer)
{
return doubleContainer._value;
}
public static DoubleContainer Create(double value)
{
return new DoubleContainer(value);
}
}
除了将它作为对象传递给函数的情况外,几乎在所有情况下都可以按预期进行转换。
如果我传入 DoubleContainer,以下代码会生成 InvalidCastException:
public double GetDouble(Object input)
{
return (double)input;
}
如果我使用动态,我可以让它工作:
public double GetDouble(Object input)
{
return (double)(dynamic)input;
}
我的这个解决方案的问题是 Visual Studio 将 (dynamic) 变灰,因为它应该是多余的,所以有人可能会删除它。另外我不知道代码库中是否还有其他地方可能会出现同样的问题。
我可以对 DoubleContainer 的实现做些什么来使我的第一个 GetDouble() 实现正常工作?我尝试添加另一个从 Object 到 DoubleContainer 的隐式转换运算符,但是 "user-defined conversions to or from a base class are not allowed"...
将对象转换为 double 时,编译器不知道它应该调用您现有的 operator double
,因此它不会插入对您的用户定义运算符 double 的任何调用。
当你在中间插入 dynamic
时,编译器会生成类似“如果这个引用有一个双精度运算符,调用它”这样的东西,所以它有效。
因此,只要将 System.Object
转换为双精度数,它实际上就无法工作,而@juharr 建议的代码稍作修改后就可以工作:
public double GetDouble(Object input)
{
if (input is DoubleContainer)
{
var dc = (DoubleContainer)input;
return (double)dc;
}
return (double)input;
}
编辑:根据@SergiyKlimkov 评论修改代码
您无法使其工作,因为您只能将已装箱的结构(这就是您使用 (double) input
所做的)拆箱为确切的底层类型,原因在此 article 中有最佳描述埃里克·利珀特着。因此,无论何时您执行 (double) someObject
- 只有对象实际上是 double
,而不是 int
,而不是 float
,而不是 DoubleContainer
,它才会起作用。如果您期望其他类型 - 您可以更好地使用 Convert.ToDouble
。为了使它与您的类型一起使用,您需要它来实现 IConvertible
:
public struct DoubleContainer : IConvertible
{
private readonly double _value;
private DoubleContainer(double value)
{
_value = value;
}
public static implicit operator double(DoubleContainer doubleContainer)
{
return doubleContainer._value;
}
public static DoubleContainer Create(double value)
{
return new DoubleContainer(value);
}
public double ToDouble(IFormatProvider provider) {
return _value;
}
public bool ToBoolean(IFormatProvider provider) {
// delegate to your double
return ((IConvertible) _value).ToBoolean(provider);
}
// ... rest is skipped ...
然后它将与
一起工作public double GetDouble(Object input)
{
return Convert.ToDouble(input);
}