C#编程中容易对Equals方法误解的几个地方

我们都知道,在C#的世界里存在两种等同性。
一种是逻辑等同性:如果两个对象在逻辑上代表同样的值,则称他们具有逻辑等同性。
另一
【菜科解读】
我们都知道,在C#的世界里存在两种等同性。
一种是逻辑等同性:如果两个对象在逻辑上代表同样的值,则称他们具有逻辑等同性。
另一种是引用等同性:如果两个引用指向同一个对象实例,则称他们具有引用等同性。
众所周知,Object类型有一个名为Equals的实例方法可以用来确定两个对象是否相等。
Object的Equals的默认实现比较的是两个对象的引用等同性。
而Object的派生类ValueTpye重写了Equals方法,它比较的是两个对象的逻辑等同性。
也就是说,在C#里,引用类型的默认Equals版本关注的是引用等同性,而值类型关注的是逻辑等同性。
当然,这并不总能满足我们的要求。
所以每当我们更在意引用类型的逻辑等同性的时候,我们就应该重写Equals方法。
重写引用类型的Equals方法以改变其默认的比较方式的一个著名例子是String类。
当我们写出“string1.Equals(string2)”这样的代码时,我们比较的不是string1和string2这两个引用所指向的是否为同一个实例(引用等同性),而是比较string1与string2所包含的字符序列是否相同(逻辑等同性)。
误解一:Equals方法和operator==具有相同的默认行为。
对于引用类型,如果没有为它重载==操作符,且其父类型也没有重写Equals方法,则这个引用类型Equals方法和operator==具有相同的默认行为,即它们比较的都是对象的引用等同性。
然而对于值类型来说,就完全不是这么回事了!因为如果你没有为自定义值类型重载operator==的话,就不能写这样的代码“myStruct1 == myStruct2”,否则会得到一个编译错误,原因是值类型没有相等操作符重载的默认实现。
误解二:自定义类的Equals的方法默认实现将自动调用operator==方法,或operator==方法的默认实现将自动调用Equals方法。
经常听到有人说某某类型是引用类型,所以它的Equals方法的默认实现将自动调用operator==方法。
这种说法完全是没有道理的。
正如上文所说的,引用类型Equals方法的默认实现来自Object,而值类型的默认实现来自TypeValue,就算他们会使用==操作符,使用的也是Object或TypeValue的重载版本。
原则上来说,只要我们没有重写一个类的Equals方法,那么它就会继承其父类的实现,而父类是没有机会使用子类型的操作符重载的。
同样,只要我们没有在一个类的==操作符重载中调用Equals方法,它是不会自动调用的。
误解三:值类型的默认Equals实现是对两个对象进行逐位比较的。
有些人认为值类型的Equals默认实现就是通过比较两个对象在内存中的位表示,即如果所有的二进制位都相等,则说明这两个对象“等同”。
这是不准确的。
因为其实值类型的Equals默认实现是对值类型的每个字段都调用该字段类型的Equals方法,如果所有字段的Equals方法都返回true,则他们才可能相等。
来看一个例子:class MyClass { public override bool Equals(object obj) { Console.WriteLine("MyClass的Equals方法被调用了。
"); return true; } } struct MyStruct { public MyClass Filed; } class Program { static void Main(string[] args) { MyStruct a; MyStruct b; a.Filed = new MyClass(); b.Filed = new MyClass(); Console.WriteLine(a.Equals(b)); } }很显然,a和b拥有完全不同的二进制位表示。
但是最终打印的结果是: MyClass的Equals方法被调用了。
True 这说明值类型的默认实现是通过调用字段的Equals方法来确定两个对象是否相等,而不是通过比较他们的二进制位是否一致来确定的。
误解四:Equals是非常基本、非常常用的方法,所以其默认的实现不存在性能问题。
对于引用类型,Equals的默认实现很简单,仅仅需要判断两个引用是不是同一种类型、两个引用指向的是不是同一块内存就可以了。
所以其性能也没有问题。
但是对于值类型,Equals的任务就没有这么简单了。
它需要对两个对象的所有字段都做出比较,即逐字段调用字段类型的Equals。
由于在ValueType(值类型Equals方法默认实现的位置)中,不可能知道它所有的子类型都包含哪些字段,所以为了调用子类型字段的Equals方法,ValueType的Equals就需要使用反射技术。
您可能已经看出来了,反射并不是一种性能友好的技术,所以值类型的Equals方法算不上高效。
这也正是为什么微软推荐我们为自定义值类型重写Equals方法的原因。
编程,中容,易对,Equals,方法,误解,的,几个,地方,
安卓编程如何实现获取本机中所有图片
在这个示例中,我使用android-support-v4.jar中的加载器来实现获取本机中所有图片,关于这个包在以前的文章中也提到,是一个非常有用的包。
先让我们看下本示例实现的效果图: 项目结构图如下所示: MyDevicePhotoActivity.java文件中代码如下:package com.device.photo;import android.app.Dialog;import android.content.ContentResolver;import android.database.Cursor;import android.graphics.Bitmap;import android.net.Uri;import android.os.Bundle;import android.provider.MediaStore;import android.view.View;import android.view.View.OnClickListener;import android.widget.AdapterView;import android.widget.Button;import android.widget.ImageView;import android.widget.ListView;import android.widget.TextView;import android.widget.AdapterView.OnItemClickListener;import android.support.v4.app.FragmentActivity;import android.support.v4.app.LoaderManager.LoaderCallbacks;import android.support.v4.content.CursorLoader;import android.support.v4.content.Loader;import android.support.v4.widget.SimpleCursorAdapter;import android.support.v4.widget.SimpleCursorAdapter.ViewBinder;/*** Android实现获取本机中所有图片* @Description: Android实现获取本机中所有图片* @FileName: MyDevicePhotoActivity.java * @Package com.device.photo * @Author Hanyonglu* @Date 2012-5-10 下午04:43:55 * @Version V1.0*/public class MyDevicePhotoActivity extends FragmentActivity implements LoaderCallbacks{ private Bitmap bitmap = null; private byte[] mContent = null; private ListView listView = null; private SimpleCursorAdapter simpleCursorAdapter = null; private static final String[] STORE_IMAGES = { MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.LATITUDE, MediaStore.Images.Media.LONGITUDE, MediaStore.Images.Media._ID }; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); listView = (ListView)findViewById(android.R.id.list); simpleCursorAdapter = new SimpleCursorAdapter( this, R.layout.simple_list_item, null, STORE_IMAGES, new int[] { R.id.item_title, R.id.item_value}, 0 ); simpleCursorAdapter.setViewBinder(new ImageLocationBinder()); listView.setAdapter(simpleCursorAdapter); // 注意此处是getSupportLoaderManager(),而不是getLoaderManager()方法。
getSupportLoaderManager().initLoader(0, null, this); // 单击显示图片 listView.setOnItemClickListener(new ShowItemImageOnClickListener()); } @Override public Loader onCreateLoader(int arg0, Bundle arg1) { // TODO Auto-generated method stub // 为了查看信息,需要用到CursorLoader。
CursorLoader cursorLoader = new CursorLoader( this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, STORE_IMAGES, null, null, null); return cursorLoader; } @Override public void onLoaderReset(Loader arg0) { // TODO Auto-generated method stub simpleCursorAdapter.swapCursor(null); } @Override public void onLoadFinished(Loader arg0, Cursor cursor) { // TODO Auto-generated method stub // 使用swapCursor()方法,以使旧的游标不被关闭. simpleCursorAdapter.swapCursor(cursor); } // 将图片的位置绑定到视图 private class ImageLocationBinder implements ViewBinder{ @Override public boolean setViewValue(View view, Cursor cursor, int arg2) { // TODO Auto-generated method stub if (arg2 == 1) { // 图片经度和纬度 double latitude = cursor.getDouble(arg2); double longitude = cursor.getDouble(arg2 + 1); if (latitude == 0.0 && longitude == 0.0) { ((TextView)view).setText("位置:未知"); } else { ((TextView)view).setText("位置:" + latitude + ", " + longitude); } // 需要注意:在使用ViewBinder绑定数据时,必须返回真;否则,SimpleCursorAdapter将会用自己的方式绑定数据。
return true; } else { return false; } } } // 单击项显示图片事件监听器 private class ShowItemImageOnClickListener implements OnItemClickListener{ @Override public void onItemClick(AdapterView ?> parent, View view, int position, long id) { // TODO Auto-generated method stub final Dialog dialog = new Dialog(MyDevicePhotoActivity.this); // 以对话框形式显示图片 dialog.setContentView(R.layout.image_show); dialog.setTitle("图片显示"); ImageView ivImageShow = (ImageView) dialog.findViewById(R.id.ivImageShow); Button btnClose = (Button) dialog.findViewById(R.id.btnClose); btnClose.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon(). appendPath(Long.toString(id)).build(); FileUtil file = new FileUtil(); ContentResolver resolver = getContentResolver(); // 从Uri中读取图片资源 try { mContent = file.readInputStream(resolver.openInputStream(Uri.parse(uri.toString()))); bitmap = file.getBitmapFromBytes(mContent, null); ivImageShow.setImageBitmap(bitmap); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } dialog.show(); } }}FileUtil.java文件主要是对图片资源的处理。
代码如下:package com.device.photo;import java.io.ByteArrayOutputStream;import java.io.InputStream;import android.graphics.Bitmap;import android.graphics.BitmapFactory;/*** 文件操作类* @Description: 文件操作类* @FileName: FileUtil.java * @Package com.device.photo * @Author Hanyonglu* @Date 2012-5-10 下午01:37:49 * @Version V1.0*/public class FileUtil { public FileUtil() { // TODO Auto-generated constructor stub } /** * InputStream to byte * @param inStream * @return * @throws Exception */ public byte[] readInputStream(InputStream inStream) throws Exception { byte[] buffer = new byte[1024]; int len = -1; ByteArrayOutputStream outStream = new ByteArrayOutputStream(); while ((len = inStream.read(buffer)) != -1) { outStream.write(buffer, 0, len); } byte[] data = outStream.toByteArray(); outStream.close(); inStream.close(); return data; } /** * Byte to bitmap * @param bytes * @param opts * @return */ public Bitmap getBitmapFromBytes(byte[] bytes, BitmapFactory.Options opts) { if (bytes != null){ if (opts != null){ return BitmapFactory.decodeByteArray(bytes, 0, bytes.length,opts); } else{ return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } } return null; } } 安卓,编程,怎么,实现,获取,本,机中,所,有图片,
ExtJS4组件化编程使用Ext+.Net动态加载,面向对象
请看代码中的注释~~使用Ext+.Net,用Direct模式传递数据Default.aspx:view sourceprint? ExtJS学习 正在加载... Ext.app.LoginDialog.jsview sourceprint?//LoginDialog类,继承Ext.Window,上层对象使用new Ext.app.LoginDialog().show()动态实例化并显示。
Ext.define(‘Ext.app.LoginDialog‘,{ extend:‘Ext.Window‘, title: ‘登陆‘, plain: true, closable: false, closeAction: ‘hide‘, width: 400, height: 300, layout: ‘fit‘, border: false, modal: true, //使用xtype: ‘LoginFormPanel‘动态实例化Ext.app.LoginFormPanel,并使用api参数指定load和submit的服务器端方法。
本例中只有submit items: {itemId: ‘loginFormPanel‘,xtype: ‘LoginFormPanel‘,api: {submit: MyApp.ChcekLogin.Check}} }); Ext.app.LoginFormPanel.jsview sourceprint?//指定远程调用的Provider,注意不能在initComponent中指定,因为config属性设置是在initComponent之前,会报api找不到错误 Ext.direct.Manager.addProvider(Ext.app.REMOTING_API); //loginForm类,继承Ext.form.FormPanel,使用alias注册至ComponentMgr,上层对象使用xtype:LoginFormPanel动态实例化。
//form的submit()方法使用Direct提交,上层对象实例化本类的时候使用config中的api属性指定服务器端方法。
//很奇怪的是不能在Ext.define或者Ext.apply中指定api属性,指定了实例化之后也会丢失,然后报url参数没有的错误,只能在上层对象实例化本类得时候使用config中的api属性指定api //如果在这里使用原始的new方法指定api也可以,是ext4中的问题?有谁知道的发mail告诉我,万分感谢~~ //使用Ext.define定义本类,定义中使用initComponent指定实例化之前需要执行的操作。
//按面向对象编程思想,本类不调用任何上层对象,方法中不指定scope: this Ext.define(‘Ext.app.LoginFormPanel‘,{ extend:‘Ext.form.FormPanel‘, initComponent: function(){ //初始化部分需要完成的功能 //alert("Ext.form.FormPanel 开始加载……"); //貌似Ext.apply得来的属性和在Ext.define中定义的没什么区别,为什么要用这个呢?谁来教教我? Ext.apply(this, { //labelAlign: ‘left‘ }); this.callParent(); }, alias:‘widget.LoginFormPanel‘, labelAlign: ‘left‘, buttonAlign: ‘center‘, bodyStyle: ‘padding:5px‘, frame: true, labelWidth: 80, items: [ { xtype: ‘textfield‘, name: ‘txt1‘, fieldLabel: ‘用户名称‘, allowBlank: false, anchor: ‘90%‘, enableKeyEvents: true, listeners: { keypress: function (field, e) { if (e.getKey() == 13) { this.nextSibling().focus(); } } //keypress } }, { xtype: ‘textfield‘, inputType: ‘password‘, name: ‘txt2‘, fieldLabel: ‘用户密码‘, allowBlank: false, anchor: ‘90%‘, enableKeyEvents: true, listeners: { keypress: function (field, e) { if (e.getKey() == 13) { this.nextSibling().focus(); } } //keypress } }, { xtype: ‘textfield‘, name: ‘txt3‘, fieldLabel: ‘验证码‘, allowBlank: false, anchor: ‘90%‘, mixLength: 6, maxLength: 6, enableKeyEvents: true, listeners: { keypress: function (field, e) { if (e.getKey() == 13) { this.ownerCt.submit(); } } //keypress } }, { xtype: ‘panel‘, height: 100, html: ‘‘, border: false }, { xtype: ‘panel‘, height: 30, html: ‘*如果图片不清晰请单击图片更换图片‘, border: false } ], //items buttons: [ { text: ‘确定‘, handler: function () { this.findParentByType(‘LoginFormPanel‘).submit(); }}, //面向本对象编程,这里不要加入 scope: this,否则function会指定到window上面 { text: ‘重置‘, handler: function () { this.findParentByType(‘LoginFormPanel‘).form.reset(); } } ], submit: function () { if (this.getForm().isValid()) { this.getForm().submit({ success: function (form, action) { window.location = "main.htm"; }, failure: function (form, action) { //使用form参数访问原submit的form form.reset(); //使用action.result访问结果集 Ext.MessageBox.alert(‘登陆失败‘, action.result.data); } }) } } }); 过程已经写到注释里面了 ExtJS4,组件,化,编程,使用,Ext+.Net,动态,