C#语法相比其它语言比较独特的地方(三)

1.在C++中允许从一个case贯穿到另一个case标签
比如:

代码如下:

int a =1;
switch(a)
{
  case 1:
     some statements;
  case 2;
     other statements;
     break;
}

第一个case和第二个case之间可以没有break
而在C#中这个break不能省略.
3.as和is只会检测待转化类型的类型,而不会进行其它操作,所以只有当转化类型是目标类型或者目标类型的子类(当然如果目标类型是接口,并且待转化类型实现了该接口也可以)才能转换成功.
强制转化,会调用系统定义(比如float转化为int类型)或自己定义的转化函数(通过implicit operator定义).

从常规的需求来看,大部分情况下,推荐使用as转换.

但要注意as不能用于value type.因为在转换不成功的时候返回null,而value type不能为
null.这时可以考虑用is.

代码如下:

object o = 1;
int i = 0;
if(o is int)
i = (int)o;

6.const是编译时常量
readonly是运行时常量
推荐使用static readonly代替const
const变量被硬编码进assembly,有版本问题.
C#只是用sealed替代了fianl,另外C#的派生类在默认情况下不会override基类方法,只有当基类方法用关键字virtual标志,并且子类显式用override标志方法时才会override.这样也有效地解决了版本问题.
7.delegate在本质上就是函数指针.

因为委托还引发了sun和MS的论战.
Anders早在Borland的时候就在Delphi中引入了delegate,后来跑到MS,弄J#的时候又添加进了delegate,Sun很不满意,说他们破坏了Java语言的特性.
到了C#,Anders终于可以名正言顺地添加delegate了.

-------------------------------

狗尾续貂(二)

1.internal protected取的是internal和protected的并集,即在同一个Assembly或者不同
的Assembly内但是继承自该类都可以访问.

3..NET FrameWork采用了字符串缓冲池技术.
比如 string a = "test";和string b = "test";a和b指向内存中的同一块区域.
另外C#中有几种判等方式,是否重写可参照如下规则:

代码如下:

public static bool ReferenceEquals( object left, object right );
public static bool Equals ( object left, object right );
public virtual bool Equals( object right);
public static bool operator==( MyClass left, MyClass right );

上述四个方法override应该遵循如下规则:

1.前两个无论如何不要override
2.第三个和第四个在自定义类型是ValueType的时候要改写
3.当自定义类型是ReferenceType的时候,如果想改变RefrenceType默认的用对象标志判等的方式,可以改写第三个

4.当自定义类型是RefrenceType的时候,最好不要改写operator==.

下面分别给出上述规则的原因:

1.

A.ReferenceEquals()这个方法当且仅当两个变量指向同一对象的时候才相等,它的实现是比较对象标志(Object Identity,任何对象在创建的时候都会生成一个OI),所以任何值类型用ReferenceEquals()比较所得到的结果都是false,即使跟自己比较也是,因为要装箱,装箱的结果肯定不是同一个对象.所以这个方法也就没有改写的必要.object的ReferenceEquals()是实现这种比较最好的方式.

B.Equals (object left, object right )这个方法用于在不知道待比较的参数是何种类型的时候.那如何比较两个变量是否相等那?很简单,把比较相等的职责交给其中一个待比较变量的Equals方法,上述方法的实现大概如下所述:

代码如下:

public static bool Equals( object left, object right )
{
  // Check object identity
  if (left == right )
    return true;
  // both null references handled above
  if ((left == null) || (right == null))
    return false;
  return left.Equals (right);
}

之所以不要重写上述两个方法,是因为两者都很好的实现了各自的语意.

2.为什么是值的时候要重写第三个方法?

ValueType是所有值类型的基类,由于要实现值相等的语意,它重写了object的Equals方法,但是在不知道派生类变量和其类型的情况下,ValueType的Equals在实现的时候只能用反射来比较,这样的效率是很低的,所以任何自定义的值类型都要重写Equals,以避免用ValueType的默认实现带来的反射的效率损失.

3.object的实例Equals方法的默认实现跟ReferenceEqual一样,是通过比较对象标志来实现的,有些ReferenceType需要值相等的语意,比如string,这是就要改写实例Equals.

4..Net FrameWork期望所有的ReferenceType的operator==保留引用语意.

相等在语意上必须满足三点

1.自反       a=a必须永远成立;
2.对称       a=b则b=a;
3.传递       a=b;b=c则a=c

代码如下:

public class Foo
{
  public override bool Equals( object right )
  {
    //1.判断待比较变量引用是否为空
    if (right == null)
      return false;
 //2.是否指向同一实例,如果是同一实例则必然相等.
    if (object.ReferenceEquals( this, right ))
      return true;

//3. 判断类型是否相同
    if (this.GetType() != right.GetType())
      return false;

// 4.比较内容
    return CompareFooMembers(
      this, right as Foo );
  }
}

第三步如果可以转化成this引用的对象不行吗?

答案是不行,必须是类型相同的.举例如下:

代码如下:

public class B
{
  public override bool Equals( object right )
  {
    // check null:
    if (right == null)
      return false;

// Check reference equality:
    if (object.ReferenceEquals( this, right ))
      return true;

// Problems here, discussed below.
    B rightAsB = right as B;
    if (rightAsB == null)
      return false;

return CompareBMembers( this, rightAsB );
  }
}

public class D : B
{
  // etc.
  public override bool Equals( object right )
  {
    // check null:
    if (right == null)
      return false;

if (object.ReferenceEquals( this, right ))
      return true;

// Problems here.
    D rightAsD = right as D;
    if (rightAsD == null)
      return false;

if (base.Equals( rightAsD ) == false)
      return false;

return CompareDMembers( this, rightAsD );
  }

}

//Test:
B baseObject = new B();
D derivedObject = new D();

// Comparison 1.
if (baseObject.Equals(derivedObject))
  Console.WriteLine( "Equals" );
else
  Console.WriteLine( "Not Equal" );

// Comparison 2.
if (derivedObject.Equals(baseObject))
  Console.WriteLine( "Equals" );
else
  Console.WriteLine( "Not Equal" );

留意一下加粗的部分就知道,如果B的CompareBMembers和D的CompareDMembers比较的元素相等的话,Comparison 1将输出Equals,而Comparison 2将永远输出Not Equal.所以在这种情况下将违反相等语意中的对称性.
所以还是老老实实在第三步Check类型哈.
上面例子中D比B多了一句:

代码如下:

if (base.Equals( rightAsD ) == false)
      return false;

很明显,基类的元素肯定要让基类去判断是否相等.但是如果某个类的直接基类是object千万必要调用base.Equals了,要不然跟没有重写实例Equals是一样的效果

4.跟C++的引用不一样,ref和out其实就是指针,在函数之间传递用的是拷贝传值.

(0)

相关推荐

  • C#语法相比其它语言比较独特的地方(一)

    由于c#是由设计.net本身的人设计出来的,所以c#才是.net上最一流的语言. 实际上,已经有一百多万行c#代码用于实现.Net class framework. 要发挥出.net平台全部的效能,只有c#才是首选. 1,switch语句可以用来测试string型的对象 这在c,c++,java等其他各主流语言中都是不可以的,唯独c#可以. 例如string a="haha"; 复制代码 代码如下: switch(a) {     case "dfj":     

  • C#语法相比其它语言比较独特的地方(二)

    之前有个兄弟给我的卷一re了帖子,我当时没有g,m,直到他把它删掉才后悔莫及,人生最痛苦的事情莫过于此...... 好,即便如此,我们还是满怀希望的向前奔去.接着写卷二. 还要提一下,上次最后说到的delegate,在我了解了它的实现和用途以及看到我的偶像的一些访问记录后,我对它有了很深的理解,并且在事件处理机制上,我更偏向于我偶像这边,有兄弟说MFC是switch,java是listener,其实java这些listener在processEvent里面还不是一个个switch然后调用,都是s

  • C#语法相比其它语言比较独特的地方(三)

    1.在C++中允许从一个case贯穿到另一个case标签 比如: 复制代码 代码如下: int a =1; switch(a) {   case 1:      some statements;   case 2;      other statements;      break; } 第一个case和第二个case之间可以没有break 而在C#中这个break不能省略. 3.as和is只会检测待转化类型的类型,而不会进行其它操作,所以只有当转化类型是目标类型或者目标类型的子类(当然如果目标

  • C语言中函数指针的三种使用方法总结

     C语言中函数指针的三种使用方法总结 在这里分享一下自己的心得,希望和大家一起分享技术,如果有什么不足,还请大家指正.写出这篇目的,就是希望大家一起成长,我也相信技术之间没有高低,只有互补,只有分享,才能使彼此更加成长. 定义方式:int (*p)(int x, int y); 实现代码: #include <stdio.h> int sum(int x, int y){ return x + y; } int reduce(int x, int y){ return x - y; } int

  • Go 语言中关于接口的三个

    我的在线博客:http://golang.iswbm.com 我的 Github:github.com/iswbm/GolangCodingTime 1. 对方法的调用限制 接口是一组固定的方法集,由于静态类型的限制,接口变量有时仅能调用其中特定的一些方法. 请看下面这段代码 package main import "fmt" type Phone interface { call() } type iPhone struct { name string } func (phone i

  • C语言求阶乘之和的三种实现方法(先阶乘再累加)

    目录 题目: 方法一:使用一层for循环实现 代码简单快捷容易理解 方法二:使用两层for循环嵌套 方法三:函数递归实现 总结 题目: 此处题目是以1-20的阶乘之和举例 方法一:使用一层for循环实现 代码简单快捷容易理解 代码示例如下: #include<stdio.h> int main() { double a = 1, sum = 0;//因为最后值可能会超出int所能接收的范围 故用double int n, i; scanf("%d", &n);//注

  • Go语言拼接URL路径的三种方法

    目录 JoinPath ResolveReference path.Join 参考 Go语言拼接URL路径有多种方法建议用ResolveReference. JoinPath JoinPath会把多个多个路径合并成一个路径,并且处理../和./,多个//合并成单个/. package main import (     "fmt"     "net/url" ) func main() {     u1 := "http://example.com/dir

  • C语言移除元素的三种思路讲解

    目录 问题描述 解题方案 思路一 思路二 思路三(最优解) 问题描述 原题链接:https://leetcode.cn/problems/remove-element/ 解题方案 思路一 思路一: 首先通过简单分析,很明显这是一道顺序表相关问题.首先能够想到的是暴力求解,即思路一:找到所有的val,每次挪动val后的数据覆盖删除val. 代码展示: int find(int*nums,int numsSize,int val) { int i=0; for(i=0;i<numsSize;i++)

  • RedHatLinux7.1中语言化完全攻略(三)

    Linux下有两种通用的输入法软件chinput和xcin,安装起来都十分麻烦.为简单起见,我们采用拿来主义,借用RedFlag(红旗)Linux下的rfinput输入法来实现RedHat 7.1下的中文输入. 网友PopWander将RedFlag Linux下的输入法移植到了RedHat 7.1下,我们可以到http://go3.163.com/~popwander/software/rf-xim-input-2.1-2.i386.rpm下载,然后执行以下指令安装:rpm -ivh --fo

  • R语言之左连接的三种实现操作

    数据处理中经常遇到表连接问题,本次介绍R语言中三种左连接方法,这三种是等价的,不过会有时间快慢问题,斟酌使用. 法一: > data0 <- merge(a,c,all.x=TRUE,by='CELLPHONE') 法二: > data1 <- sqldf('select a.*,b.* from a left join c on a.CELLPHONE=c.CELLPHONE') 法三: > data2 <- c[a,on='CELLPHONE'] 注意:第三种方法的

随机推荐