假设我有一个班级Point
:
class Point
{
public double? x, y;
}
为了表明x
或y
是未知的,我已将其类型设为可为空。
现在,在数学表达式中使用这些值时,每次必须将其值转换为双倍是不方便的。即Math.Sin(p.x)
将产生编译时错误;你必须改为投射它:Math.Sin((double)p.x)
。
我解决转换问题的方法是使包装器只读属性执行转换:
class Point
{
public double? x, y;
public double X { get { if (x != null) return (double)x; else throw new Exception(); } }
public double Y { get { if (y != null) return (double)y; else throw new Exception(); } }
}
这是一个好方法吗?
答案 0 :(得分:5)
您可以使用Value
的{{1}}属性:
Nullable
或
Math.Sin(p.x.Value)
如果未设置该值,则会抛出异常,因此等同于您的代码。
关于方法的相对优点:
IMO基于属性的方法可以使消费者免受public double X { get { return x.Value; } }
内部存储为x
以表示未知问题的实现细节。如果只希望消费者处理“已知”价值,这将是我的偏好。但是,如果消费者需要满足Nullable
仍然“未知”的情况,那么暴露x
属性将是最佳选择(任何消费者都可以使用Nullable
属性进行检查HasValue
)。
答案 1 :(得分:2)
我个人并不是尝试访问属性时抛出的泛型异常的忠实粉丝(特别是在访问InvalidOperationException
.Value
时抛出的泛型Nullable<T>
当HasValue == false
)时。我会反而暴露布尔属性,指定是否定义了值:
在尝试访问属性之前,开发人员会检查成员的有效性,这将是合同的一部分:
public class Point
{
private double? x;
private double? y;
public bool IsXDefined { get { return this.x.HasValue; } }
public bool IsYDefined { get { return this.y.HasValue; } }
// Returns an undefined value (default(double)) in case X is not defined
// This is a part of the contract that X is valid if, and only if IsXDefined is true.
public double X { get { return x ?? default(double); } }
public double Y { get { return y ?? default(double); } }
}
Point p = new ...
if (p.IsXDefined)
{
double zz = p.X ....
}
实际上,针对同一目标有不同的方法,但重要的是开发人员知道代码实际上做了什么。一个例外是让开发人员知道他试图阅读的财产处于良好状态的好方法。
在上面的代码中,X属性的文档会说&#34; Point的X坐标(如果已定义)。要确保X
是有效值,请在尝试访问此属性之前检查IsXDefined
是否返回&#34;。
PointCoordinateNotDefinedException
也很棒:
public double X
{
get
{
if (this.IsXDefined)
{
return this.x.Value;
}
throw new PointCoordinateNotDefinedException();
}
}
合同的相同内容:X属性的文档会说&#34; 如果未定义坐标,则可以抛出PointCoordinateNotDefinedException
异常。在尝试访问此属性&#34;。
IsXDefined
是否返回true
对我来说两者都没关系,也许异常更好你是对的,因为如果未指定值会导致应用程序崩溃(这将阻止应用程序以不正确的值运行(比调查更难调试) stacktrace来查看抛出异常的内容))。
但是,我不认为尝试访问属性直到他们抛出异常(同时捕获这些异常)是一个好主意。我更喜欢在访问属性之前测试IsXDefined
布尔值。