随笔 - 14  文章 - 0 评论 - 18 trackbacks - 0



Recently I got a classic WD 740GD hard disk (Raptor II), and I decided not to reinstall but move the whole Vista system to this monster. I thought this is simple, but at last, this cost me 3 nights. The main problem is, I can't change the new system drive's letter either under under windows or DOS (there is a command set drive letter in DOS, but I guess it only changes drive letters in DOS). However the final solution is like this:

First, let's define 2 functions to simplify the description(Here drive has equivalent meaning with partition):

Dump(C,D)  means dump partition C: to partition D: with Ghost.

SetDefault(C) means start system with Vista Installation Disk, choose Fix computer problems, this will set the partition contains windows as startup partition.

I formatted and assigned W: for the new disk under Vista. I Dump(C,W) and then SetDefault(W), and unplugged the old disk, try to start with new disk alone, but failed, because the new disk's drive letter is W: and lots of system files' paths are hard coded to C:. So I have to keep the old system in C:. And I also can't switch C: with W:, because either you start the system with C: or W:, one of them is in use.

I plugged the new disk to another computer, split it to 2 partition by Acronis Disk Director, the new partition, lets call it X:, I then Dump(W,X).  Now I have 3 system partitions: C: on old disk, W: and X: on new disk, what I planned is to start with W:, switch the driver letter X: with C:, then SetDefault(C:), so drive letter C: is now in new disk. However, to my surprise, when I plugged the new disk back to my computer, the 2 system partition were automatically recognized as C: and D:! So I don't have to switch the drive letter. I guess Acronis updated the drive letters while do the split. All I did later is formatting new D: and merge the space to C:. Now I have a completely same system with drive letter C: on new disk :)

 

posted @ 2008-05-15 00:03 阿鸡米得 ㊣ 阅读(109) | 评论 (0)编辑

     手上有一个DataGridView,绑定了一个BindingList。BindingList声明如下:
private BindingList<Report> reportsInGrid;

     Report有一个property名为DisplayOrder,现在需要在DisplayOrder改变时立即对List重新排序。拟在CellEndEdit事件中处理排序。
     注意到BindingList本身并没提供Sort方法,而DataGridView本身提供了两个重载的Sort()方法,于是做了如下尝试:
     方法1:DataGridView的第一个Sort方法签名如下:
public virtual void Sort(IComparer comparer)
     使用后提示当DataGridView的virtualMode为true时不能使用该方法排序。失败。

     方法2:第二个Sort方法签名如下:
public virtual void Sort(DataGridViewColumn dataGridViewColumn, ListSortDirection direction)
     使用后提示异常数据源不可排序。失败

     方法3:正解。声明一个List,将BindingList传入作为构造函数,对List进行排序再将元素添加回BindingList,代码如下:
            private void SortReportsByDisplayOrder(ref BindingList<Report> reportList)
        
{
            List
<Report> list = new List<Report>(reportList);
            ReportHelper.SortReportList(
ref list);
            reportList.Clear();
            
foreach (Metadata.Report rpt in list)
            
{
                reportList.Add(rpt);
            }

        }
    


        
public static void SortReportList(ref List<Report> reportList)
        
{
            reportList.Sort(
delegate(Report x, Report y)
                    
{
                        
//First sort by category id, then display order, so report in same category gathered and sorted.
                        int result = x.CategoryID - y.CategoryID;
                        
if (result == 0)
                            
return x.DisplayOrder - y.DisplayOrder;
                        
else
                            
return result;
                    }
);
        }

          经测试,工作正常。




posted @ 2008-05-07 18:33 阿鸡米得 ㊣ 阅读(90) | 评论 (0)编辑


      昨天碰到一个很费解的问题。我们在程序里使用了ReportViewer控件,当调用了控件RefreshReport方法后,整个Application的UI响应都变得很慢。尝试将引用升级到ReportViewer 9.0,也不能解决问题。
      排除了其他可能后,MSDN搜索ReportView + RefreshReport关键字,得知reportviewer存在一个bug,该assembly被加载后不能从app domain里正常的unload,有可能造成内存泄露。
       一个workaround是在执行RefreshReport方法前先调用reportViewer.LocalReport.ExecuteReportInSandboxAppDomain() ,使其工作在一个独立的app domain里,避免影响进程的main app domain。
      虽然内存泄露不是我们的情况,但还是尝试了一下,果然问题解决了,费解,不知道原因。



posted @ 2008-04-15 10:43 阿鸡米得 ㊣ 阅读(53) | 评论 (2)编辑
     摘要: 这篇曾经贴在自己的live space上,今天整理出来发在这里。 内容参考了《重构》和《设计模式》Case如下,假设Employee类中有这样两个方法: PayAmount. 根据员工类型获得员工的薪水: publicintPayAmount(EmployeeTypeempType){switch(empType){caseEmployeeType.ENGINEER:returnm_basicSa... 阅读全文
posted @ 2008-04-04 22:04 阿鸡米得 ㊣ 阅读(1975) | 评论 (13)编辑

System.Collections命名空间下的BitArray相当于一个bool类型的数组bool[]。MSDN用BitArray做例子实现了一个索引器,今天研究了一下这个例子,对原来的实现做了一些改变,将>> 5操作变成了mod 32,敝人以为这样看起来会更直观。
代码如下:
    public class MyBitArray
    
{
        
private int[] m_Bits;
        
private int m_Length;

        
public MyBitArray(int length)
        
{
            
//Comment 1 
            m_Length = (length - 1/ 32 + 1;  
            m_Bits 
= new int[m_Length];
        }


        
public bool this[int index]
        
{
            
//Comment 2
            get return (m_Bits[index / 32& 1 << index) != 0; }
             
set
            
{
                
if (value)
                    m_Bits[index 
/ 32|= 1 << index;
                
else
                    m_Bits[index 
/ 32&= ~(1 << index);
            }

        }

    }


Comment 1 :1个int占用4个字节,可以存储32个bit值。所以实际所需的int数组的长度是所传入length的1/32。length -1 是考虑到边界值32,(32-1)/32 + 1 =1,否则得到的结果是2,而此时只需1个int长度的内存即可。而对边界值0,(0-1)/32 + 1 = 1,也是没有问题的。

Comment 2 :BitArray中的一系列bit值,可以看做被分段存储在了int元素中,index/32取出了index对应的bit值所在的int元素。再将1移动到合适的位上,进行与/或操作。看起来有点类似内存访问中段和页的概念。


关于索引器:索引器与property最大的区别在于,property只有set方法带有参数,而indexer的get和set方法都带有默认参数index。另外property可以是static的,索引器当然不行。详细比较请看这里

关于位操作:通常,当指定移动的位数溢出的时候,位移操作会自动对移动位数取模。这就是为什么上面的set方法中1 << index能将1移动到正确位置的原因。
举例,8 >> 33; 相当于8 >> 1, 结果是4,而不是0。
1 << 32; 相当于1 << 0, 结果还是1。

有意思的是,变量类型为byte或short或int的时候,CLR取模的除数都是32,而不是随数据类型的长度变化。比如:
short b = 1; b <<= 16; 
得到的结果为0. 此时CLR并非对16取模。
但如果数据被定义为long,则取模的除数会自动变成64。
long l = 1; l <<= 32; 得到的值是4294967296,即2^32。







posted @ 2008-03-30 11:46 阿鸡米得 ㊣ 阅读(125) | 评论 (0)编辑

as关键字最大的好处就是,当转换不能进行的时候,会返回一个空值,而不是抛异常。
其实现类似于这样的语句:expression is type ? (type)expression : (type)null。

一个应用场景是,DataGridView中cell的value是一个object类型。如果想判断用户的输入是不是空串,需要这样判断:
If((cell.Value != null)
&&(!string.IsNullOrEmpty(cell.Value.ToString())
{
    
// process the value
}

使用as关键字可以使上面的代码更简洁:
If(!string.IsNullOrEmpty(cell.Value as string))
{
   
// process the value
}
如果不允许用户输入全为空格,就不适合用as了,因为需要在检查IsNullOrEmpty前调用Trim函数。


需要注意的是,as关键字后的目标类型只能是引用和nullable类型,不能是int,double等值类型,类似 obj as int 这样的语句编译将不能通过。



posted @ 2008-03-23 16:57 阿鸡米得 ㊣ 阅读(16) | 评论 (0)编辑

解决了上午提到的 Build Error: The command ****** exited with code 9009 以后,又碰到一个新问题。在post build里尝试注册assembly失败,提示如题。
google之,发现主要是VISTA用户反映存在这个问题,跟我的情况没什么关系。

最终发现原因是VS 不能使用.NET Framework安装目录下的gacutil工具! (之前把两个framework的安装目录都添加到了环境变量里)
将环境变量设为:D:\program files\microsoft visual studio 8\sdk\v2.0\bin就OK了。
仔细想想,因为当时.NET 2.0的安装目录下是没有gacutil的,将.NET 1.1的安装目录添加到了环境变量中。当时的想法是,gacutil是早已有之的工具了,所以.NET 2.0下没有它,意思是继续沿用1.1版的就好了。但现在看来1.1的gacutil显然是不能注册2.0的assembly的。




posted @ 2008-03-21 20:20 阿鸡米得 ㊣ 阅读(103) | 评论 (0)编辑


build某个project的时候提示了这个错误,google到这篇文章:http://support.microsoft.com/kb/908268
提示将regasm的完整路径写在命令里,如:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe

看来是VS2005不能取到.NET Framework的安装路径。
于是将pose-build命令改为:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /unregister "$(TargetPath)",build通过,不过圡了点...

更好的做法是将.NET的安装路径"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727" 和 "D:\program files\microsoft visual studio 8\sdk\v2.0\bin"加入系统环境变量里的PATH变量里,然后重启。这样所有的utils工具就都能在post-build event里直接访问了


posted @ 2008-03-21 10:53 阿鸡米得 ㊣ 阅读(99) | 评论 (0)编辑

    1. Don't add/delete elements in a foreach cycle.  
        不要在foreach遍历循环里做添加删除元素的操作。

    2. When remove multi elements, remove from the last element, not the first.  
        删除多个元素的时候,从最后一个元素开始删除,而不是第一个,这样不会影响队列中待删除的其他元素。

posted @ 2008-03-14 13:47 阿鸡米得 ㊣ 阅读(20) | 评论 (0)编辑

 
    使用.NET也有些日子了, 但对event这个东西一直没有去深入认识. 这两天因为想在DataGridView的cell里实现一个AutoComplete的功能, 趁机了解了一下, 下面贴上一点心得.

    Event机制其实是一个典型的Observer pattern: Publisher抛出一个事件, 所有订阅了该事件的Subscriber对此做出各自的响应. 
    简单看下这个过程:

    Publisher:

        public class Publisher
        
{
            
//Publisher exposes an event for subscribers.
            public event EventHandler OnPublish;

            
public void InvokeMe(EventArgs e)
            
{
                
//Publisher trigger this event.
                if (OnPublish != null)
                    OnPublish(
this, e);                
            }

        }

        Subscriber:

        public class Subscriber
        
{
            
private Publisher m_Pub;

            
//Subscriber expose a property to hook up a publisher's event.
            public Publisher MyPublisher
            
{
                
get return m_Pub; }
                
set 
                

                    m_Pub 
= value;
                    m_Pub.OnPublish 
+= PublishHandler;
                }

            }


            
//Subscriber's behavior to publisher's event.
            private void PublishHandler(object sender, EventArgs e)
            
{
                Console.WriteLine(
string.Format("Subscriber response to {0}",sender.ToString()));
            }

        }

        跑一下:
        static void Main(string[] args)
        
{
            Publisher publisher 
= new Publisher();
            Subscriber subscriber 
= new Subscriber();
            
//Subscriber subscribe a publisher.
            subscriber.MyPublisher = publisher;
            
//Trigger publisher's event.
            publisher.InvokeMe(new EventArgs());
            Console.ReadLine();
        }
   

    使用reflector 查看源代码, 发现CLR自动为Publisher类生成了一个EventHandler类型的成员:
    private EventHandler OnPublish;

   以及两个操作方法:
public void add_OnPublish(EventHandler value)
{
    
this.OnPublish = (EventHandler) Delegate.Combine(this.OnPublish, value);
}


public void remove_OnPublish(EventHandler value)
{
    
this.OnPublish = (EventHandler) Delegate.Remove(this.OnPublish, value);
}

    
    看起来OnPublish和普通的delegate对象使用上并没有什么区别.
至此,有了一个疑问: 既然使用delegate就可以实现整套Event机制了, 那么event关键字的意义何在呢? 

    我觉得是为了代码可读性. 一个被event修饰的delegate类型更加醒目, 提示着这个delegate类型的用途. 另外编译器会为event关键字修饰的delegate类型自动生成成员变量, 使代码看起来更加简洁直观. 不知道还有没有什么历史原因, 暂且就先认为是这两条吧. 

    至此, 整个Event机制比较明了了, 简单说来, 就是subscriber把自己的函数地址赋给了一个特定的publisher的函数指针变量而已. 而在.NET Framework中, 这些地址就是一个个delegate对象了. 为了简化问题, 示例没有使用自定义的EventHandler, 关于自定义EventHandler可以参考 How to: Publish Events that Conform to .NET Framework Guidelines 这篇文章. 

   



后记:今天查阅了一下 CLR via C#. 事实上, CLR对event关键字的实现有两点好处是直接使用delegate所没有的:
第一, 注意到生成的OnPublish变量是private的, 外界对它的访问只能通过add_OnPublish和remove_OnPublish这两个方法(+=和-=两个操作符最终会被编译成对这两个方法的调用). 显然, 这样对OnPublish的封装更好, 如果直接暴露一个public的delegate, 则有可能因为一个subscriber误操作使整个委托链表被清空, 影响到其他的subscriber.

第二, 注意到add_OnPublish和remove_OnPublish都带有[MethodImpl(MethodImplOptions.Synchronized)]属性, 表明它们是线程安全的, 多个subscriber不能同时访问这两个方法.

我想,这两点才是使用event关键字的优势所在吧。



posted @ 2008-02-13 04:30 阿鸡米得 ㊣ 阅读(238) | 评论 (3)编辑