【发布时间】:2011-04-18 11:00:52
【问题描述】:
一个例子是:
XNamespace ns = "my namespace"
为什么不呢?:
XNamespace ns = new XNamespace ( "my namespace" )
使用隐式/显式转换而不是构造函数背后的想法是什么?方便吗?
有这方面的指导方针吗?
【问题讨论】:
标签: c# .net constructor type-conversion base-class-library
一个例子是:
XNamespace ns = "my namespace"
为什么不呢?:
XNamespace ns = new XNamespace ( "my namespace" )
使用隐式/显式转换而不是构造函数背后的想法是什么?方便吗?
有这方面的指导方针吗?
【问题讨论】:
标签: c# .net constructor type-conversion base-class-library
方便吗?
或多或少,是的。考虑当您有一个类似数字的对象(例如,Complex)进行计算时的情况。显然,编写代码如:
Complex result = c1 * new Complex(2) + new Complex(32);
非常烦人且难以阅读。隐式转换在这里有所帮助(在此示例中,替代方法是运算符重载,但这会导致许多类似的重载)。
有这方面的指导方针吗?
提供尽可能少的隐式转换,因为它们可能会隐藏问题。隐式转换减少显式性的程度与它们增加简洁性的程度相同。有时这很好,但有时不是。
我发现最好将隐式转换限制为非常相似的类型,例如我上面示例中的类似数字的对象:int 本质上是-a Complex(来自数学观点;即使它不是通过继承建模的),因此隐式转换是有意义的。
在 VB 中,隐式转换称为“Widening”(与 Narrowing 相反,即explicit),这很好地描述了它:在转换过程中不会丢失任何信息。
此外,操作符本质上是一个构建器函数,与构造器相比,它具有(部分)构建器函数的通常优势:即,它可以重用缓存值,而不是总是创建新实例。
以我的Complex 为例。我们可能希望缓存常用复数的值:
Class Complex {
// Rest of implementation.
private static Complex[] cache = new[] {
new Complex(-1), new Complex(0), new Complex(1) };
public implicit operator Complex(int value) {
if (value >= -1 && value <= 1)
return cache[value];
else
return new Complex(value);
}
}
当然,这种微优化是否有效是另一个问题。
【讨论】:
Complex 实现运算符 implicit operator Complex(int value)。
我相信对 XName 等简单类型使用隐式转换的原因之一是调用方法的方便。
例如,你可以写
var info = root.Elements ("user").Element ("info").Value;
提取数据的简单性是 LINQ 的全部意义所在,如果我们必须编写的话
var info = root.Elements (new XName ("user")).Element (new XName ("info")).Value;
即使对于最简单的查询,LINQ 对于复杂的查询是否完全值得?
这里的另一个重要问题是 XName 是原子化的。 见MSDN:
XName 对象保证被原子化;也就是说,如果两个 XName 对象具有完全相同的命名空间和完全相同的本地名称,它们将共享同一个实例。为此,还明确提供了相等和比较运算符。
除其他好处外,此功能还可以更快地执行查询。在过滤元素或属性的名称时,谓词中表示的比较使用身份比较,而不是值比较。确定两个引用实际上是指同一个对象比比较两个字符串要快得多。
您不能在构造函数中提供原子化,但是定义一个转换可以让您从池中挑选相应的对象并将其返回,就像它是一个新实例一样。
【讨论】:
使用隐式/显式转换是方便的问题,许多编程指南建议您避免使用显式ConvertToXXX 方法。
其中一个问题是隐式/显式转换的使用进一步重载了强制转换运算符的函数。它赋予了它双重目的
不幸的是,C# 已经在其他领域(使用原语和装箱)实现了后者。
【讨论】:
如果两个类应该可以相互转换,但它们不共享自动允许这种行为的基类的接口,则可以使用转换。隐式转换应该绝不有数据丢失的可能性;它们通常被认为是“扩大”转换。例如,将int 转换为long 是一种扩展转换,并且隐式转换没有固有的问题。显式转换可能涉及数据丢失的可能性; long 可能会也可能不会转换为 int,具体取决于其值。
我使用隐式转换的一个技巧是当我没有其他合理的选择时,将不同命名空间中的类相互转换。例如,一个 WCF 服务返回一个 AuthenticationToken 对象,我需要将该对象传递给不同命名空间中的 WCF 服务。两者都有这个 AuthenticationToken 对象,并且不断的转换会很痛苦。我的解决方案涉及在部分类中使用public static implicit operator 来添加功能以进行各种方式的转换。
【讨论】:
SimpleType 表示ComplicatedType,其中“复杂”属性都匹配默认值,则应仅允许从SimpleType 到ComplicatedType 的扩大转换,但如果它表示复杂属性值未知的ComplicatedType ,如果有的话,应该只允许隐式转换。
就个人而言,当我知道 rhs 可以转换为类的静态成员时,我会使用转换(比如说 color = "red" 来暗示 color = Colors.Red)
当我打算实际创建一个新实例时,我使用 new 运算符。
【讨论】: