扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
问:为什么引用类型不是多态类型?
答:请考虑以下代码:
using System; class Dog { public string Name; } class Test { public static void Swap(ref object a, ref object b) { object temp; temp = a; a = b; b = temp; } public static void Main() { Dog d1 = new Dog(); d1.Name = "fido"; Dog d2 = new Dog(); d2.Name = "rex"; Swap(ref d1, ref d2); } }
编译器将对 Swap() 函数调用时报告错误。为什么?请考虑 Swap 函数采用以下形式的情况:
public static void Swap(ref object a, ref object b) { a = 5; b = “Hello“; }
如果编译器允许使用上述代码,这将意味着将装箱的 int 类型值赋给 Dog 对象,这显然不是类型安全的。
[作者:Eric Gunnerson]
发布时间:2004 年 4 月 8 日 14:55:00 GMT 星期四 评论(11)
Equals 方法只是在 System.Object 中定义的一个虚拟方法,它由任何选择执行该任务的类所重写。== 运算符是一个可由类重载的运算符,该类通常具有恒等行为。
对于未重载 == 的引用类型,该运算符会比较两个引用类型是否引用同一对象,而这恰好是 System.Object 中的 Equals 实现所做的工作。
对于未重载 == 的值类型,该运算符会比较这两个值是否"按位"相等,即是否这两个值中的每个字段都相等。当您对值类型调用 Equals 时,仍然会发生这一情况,但这一次,该实现是由 ValueType 提供的,并且使用反射进行比较,从而使比较速度比特定于类型的实现慢很多。
到此为止,二者是如此类似。二者之间的主要区别是多态。运算符被重载而不是被重写,这意味着除非编译器知道调用更为具体的版本,否则它只是调用恒等版本。为阐明这一点,请看下面这个示例:
using System; public class Test { static void Main() { // Create two equal but distinct strings string a = new string(new char[] {'h', 'e', 'l', 'l', 'o'}); string b = new string(new char[] {'h', 'e', 'l', 'l', 'o'}); Console.WriteLine (a==b); Console.WriteLine (a.Equals(b)); // Now let's see what happens with the same tests but // with variables of type object object c = a; object d = b; Console.WriteLine (c==d); Console.WriteLine (c.Equals(d)); } }
结果是:
True True False True
第三行是 False,原因在于编译器不知道 c 和 d 的内容都是字符串引用,因而只能调用 == 的非重载版本。因为它们是对不同字符串的引用,所以恒等运算符返回 False。
那么,应该如何区别使用这些运算符呢?我的准则是:对于几乎所有引用类型,当您希望测试相等性而不是引用一致性时,请使用 Equals。例外的情况是字符串 - 使用 == 比较字符串确实会使事情简单得多,而且代码可读性更好,但是 您需要记住,该运算符的两端都必须是类型字符串表达式,才能使比较正常进行。
对于值类型,我通常使用 ==,因为除非值类型本身包含引用类型(这种情况极为罕见),否则是恒等还是相等的问题无关紧要。
[作者:Jon Skeet]
发布时间:2004 年 3 月 29 日 15:56:00 GMT 星期一 评论(2)
问:在我使用局部变量以后,应该将 null 赋给它们吗?
例如:
string s = ...; Console.WriteLine(s); s = null;
答:在 C# 中,很少需要对局部变量执行该操作。
变量的生存期由 JIT 跟踪,JIT 会分析变量在例程中的使用方式,准确地了解何时不再需要该变量,并且知道此后可以回收该值。
有意思的是,如果您将 null 赋给它,实际上会稍微延长该变量的生存期,从而可能导致对它进行垃圾回收的时间拖后(尽管实际上这不大可能有什么真正的区别)。
对于绝大多数方法而言,这都是真的。如果您有一个方法,该方法中的代码生存一段时间(例如,在独立的线程中执行循环),则查看一下在您等待的过程中,变量中是否有不需要的值生存可能是有意义的。
[作者:Eric Gunnerson]
发布时间:2004 年 3 月 26 日 12:15:00 GMT 星期五 评论(5)
问:在调用委托之前,为什么需要执行 null 测试?
答:如果在类中有事件,则在调用委托之前,需要添加 null 测试。通常,您编写以下代码:
if (Click != null) Click(arg1, arg2);
这里实际上可能存在争用条件 - 事件可能在第一行和第二行之间被清除。您实际上希望编写以下代码:
ClickHandler handler = Click; if (handler != null) handler(arg1, arg2);
(通常情况下)。在其他场合下,您可能需要进行某种其他种类的同步。
让我们回到主要问题。为什么需要进行 null 测试?
我们不能通过委托来更改调用的现有行为,因为某些应用程序可能依赖于它,所以必须对语言进行增强。
我们已经讨论过通过向语言中添加 Invoke() 关键字来使这一问题变得简单,但是在经过深入的讨论之后,我们断定我们无法做到任何时候都不犯错误,所以我们决定什么都不做。
查看本文来源如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者