比较C#和Ruby在比较对象的平等性

214 阅读5分钟

比较C#和Ruby在比较对象的平等性

C#和Ruby在比较对象的平等性方面有着相似的语法。两者都使用运算符equals(==)和至少一种方法来比较。Ruby使用equal?eql?,C#使用Equals。另外,两者都支持重写equals (==)操作符,以便在需要时提供不同的逻辑。这些方法的名称不同,但它们的工作原理基本相同。

理解这两种语言的区别其实很简单。如果你已经知道了引用类型和数值类型之间的区别,那么你就基本掌握了。

红宝石

方法等于?

用来测试两个对象中的引用相等的方法。比如说。

#!/usr/bin/env ruby

a = 0
b = 0.0
c = b
d = e = 0

# "false" pointer c points to b, and b and a 
# are different types.w
puts "c.equal?(a) #{c.equal?(a)}" 
# "false" b and a are different types
puts "b.equal?(a) #{b.equal?(a)}" 
# "true" Same type, same value. 
puts "d.equal?(e) #{d.equal?(e)}"</pre>

方法eql?

的同义词,不是严格的类型转换。注意哈希类使用这个方法来创建哈希,所以如果两个值是相同的,哈希方法应该返回相同的值。

#!/usr/bin/env ruby

a = 0
b = 0.0
c = b
d = e = 0

# "false" Pointer c points to b, and b and a are different types
puts "c.eql?(a) #{c.eql?(a)}" 
# "false" Different types
puts "b.eql?(a) #{b.eql?(a)}" 
# "true" Same type, same value. 
puts "d.eql?(e) #{d.eql?(e)}"

操作符等价(==)。

默认情况下,在Object类中,它是equal?的同义词。测试引用平等。

#!/usr/bin/env ruby

a = 0
b = 0.0
c = b
d = e = 0

# "true" Even when pointer c points to b, and b and a 
# are different types, the value is the same
puts "c == a #{c == a}" 
# "true" Type is casted to allow comparing them
puts "b == a #{b == a}" 
# "true" Same type, same value. 
puts "d == e #{d == e}"

C#

在解释平等选项之前,请注意Ruby和C#之间的一个重要区别。

首先,Ruby是一种动态类型的语言。在声明变量时,没有变量类型含义,所有的变量都可以根据情况用来识别不同类型的实例。例如,我们可以定义一个变量x充当字符串,然后用同一个变量x充当整数,这并不意味着我们将字符串转换为整数,这意味着我们将同一个指针(变量x)用于两种不同的类型,字符串和整数,指向内存中两个不同的地址。比如说。

#!/usr/bin/env ruby

a = "I'm string"
# Output: "a Value: 'I'm string' Class: 'String'"
puts "a Value: '#{a}' Class: '#{a.class}'"

# Output: "a Value: '10.0' Class: 'Float'"
a = 10.0
puts "a Value: '#{a}' Class: '#{a.class}'"</pre>

C#是一种静态类型的语言,所有的变量都必须在实例化一个对象之前表明其类型。例如,当声明一个字符串类型的变量x时,你将能够创建一个字符串的实例,只是,没有办法在同一范围内将x"重用 "为一个整数。试着编译下面的例子,它将会失败。

public class RubyAndCSharp {
	
	public static void Main (string []args) {
		string x = "I'm string";
		System.Console.WriteLine ("a Value: '{0}' Class: '{1}'", x, x.GetType ());

		x = 10.0; // It will fail here: "error CS0029: Cannot implicitly convert type `double' to `string'"
		System.Console.WriteLine ("a Value: '{0}' Class: '{1}'", x, x.GetType ());
	}
}

第二,内存管理。两种语言都是自动管理内存:默认情况下,所有的内存都是自动创建和释放的,不需要显式释放或分配内存,除非程序员想这样做。然而,在C#中,类型之间有一个 "区别"。有两个类型类别值类型引用类型。与内存使用有关的区别是它们的工作方式和它们使用的内存地址。声明值类型会自动分配内存,声明引用类型会声明一个指针,当变量所指向的对象被实例化时,内存会被分配。值类型是在堆栈中分配的,而引用类型是在中分配的。

这个区别真的很重要。比较两个不同 "类别 "的objects 实例,一个是值类型,一个是引用类型,是行不通的,只会失败。就像比较一个苹果和一个桔子。就像比较一个存储在堆栈中的值和一个存储在堆中的值。我们不能在不写任何额外代码的情况下比较它们。

而这个额外的代码意味着使用基类object 作为不同类型的指针,因为这两种类型,值类型和引用类型,都是对象的子类,以这种或那种方式。让我们试着编译下面这个例子。

public class RubyAndCSharp {
	public static void Main (string []args) {
		object x = "I'm string";
		// Output: "a Value: 'I'm string' Class: 'System.String'"
		System.Console.WriteLine ("a Value: '{0}' Class: '{1}'", x, x.GetType ());

		x = 10.0;
		// Output: "a Value: '10' Class: 'System.Double'"
		System.Console.WriteLine ("a Value: '{0}' Class: '{1}'", x, x.GetType ());
	}
}

在这个简短(或漫长?)的解释之后,我们准备看看谈论方法。

方法Object.Equals()

用来测试引用类型中的引用相等和值类型中的位相等。比如说。

public class RubyAndCSharp {

	class MyClass {
		public string Name { get; set; }
		public override string ToString () { return Name; }
	}
	
	public static void Main (string []args) {
		// object.Equals in Reference Types uses address memory
		MyClass myClass0 = new MyClass () { Name = "test" };
		MyClass myClass1 = myClass0;

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myClass0, myClass1, object.Equals (myClass0, myClass1));
		
		// Let's try again. This will return false. myClass1 and myClass2 are different instances
		myClass1 = new MyClass () { Name = "test" };

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myClass0, myClass1, object.Equals (myClass0, myClass1));
		
		// It doesn't matter myInt0 and myInt1 are different variables, equality will be true.
		int myInt0 = 1; 
		int myInt1 = 1;

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myInt0, myInt1, object.Equals (myInt0, myInt1));
	}
}

操作符等价(==)

基本上是object.Equals的同义词,适用相同的规则。

public class RubyAndCSharp {

	class MyClass {
		public string Name { get; set; }
		public override string ToString () { return Name; }
	}
	
	public static void Main (string []args) {
		// == in Reference Types uses address memory
		MyClass myClass0 = new MyClass () { Name = "test" };
		MyClass myClass1 = myClass0;

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myClass0, myClass1, myClass0 == myClass1);
		
		// Let's try again. This will return false. myClass1 and myClass2 are different instances
		myClass1 = new MyClass () { Name = "test" };

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myClass0, myClass1, myClass0 == myClass1);
		
		// It doesn't matter myInt0 and myInt1 are different variables
		int myInt0 = 1; 
		int myInt1 = 1;

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myInt0, myInt1, myInt0 == myInt1);
	}
}

操作符Object.ReferenceEquals()

很直接,测试引用。

public class RubyAndCSharp {

	class MyClass {
		public string Name { get; set; }
		public override string ToString () { return Name; }
	}
	
	public static void Main (string []args) {
		// Object.ReferenceEquals in Reference Types uses address memory
		MyClass myClass0 = new MyClass () { Name = "test" };
		MyClass myClass1 = myClass0;

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myClass0, myClass1, System.Object.ReferenceEquals (myClass0, myClass1));
		
		// Let's try again. This will return false. myClass1 and myClass2 are different instances
		myClass1 = new MyClass () { Name = "test" };

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myClass0, myClass1, System.Object.ReferenceEquals (myClass0, myClass1));
		
		// This will also return false.
		int myInt0 = 1; 
		int myInt1 = 1;

		System.Console.WriteLine ("object.Equals('{0}','{1}') = {2}", myInt0, myInt1, System.Object.ReferenceEquals (myInt0, myInt1));
	}
}

科罗芬

有时你必须使用一个对象引用来引用两种类型,值和引用,如果你打算比较它们的值,你必须使用静态方法object.Equals(a,b)。使用运算符equals(==)将总是返回false,因为有boxing/unboxing的问题。

public class RubyAndCSharp {
	public static void Main (string []args) {
		string str0 = "hola";
		string str1 = "hola";
		
		object obj0 = str0;
		object obj1 = str1;

		System.Console.WriteLine ("Equals: {0}, Using ==: {1}, object.Equals {2}", 
		                          obj0.Equals (obj1), // True
		                          obj0 == obj1, // True
		                          object.Equals (obj0, obj1)); // True
		                          
		bool bool0 = true;
		bool bool1 = true;
		
		obj0 = bool0;
		obj1 = bool1;

		System.Console.WriteLine ("Equals: {0}, ==: {1}, object.Equals {2}", 
		                          obj0.Equals (obj1), // True
		                          obj0 == obj1, // False
		                          object.Equals (obj0, obj1)); // True
		                          
	}
}