知秋一叶

日出而作,日入而息,凿井而饮,耕田而食,帝力何有于我哉
随笔 - 55, 评论 - 312, 引用 - 79

导航

标签

每月存档

最新留言

广告

More on "More on Strings"

贴了关于.NET String ImmutableImplementaion的blog,收到下面两个回复:

  • 对于定义如 public void xx(string yy);的函数,传进去的yy的值是改不了的.
    下面的程序展示了如何在Native函数中修改字符串参数的值:
        //Native.cpp: the exported native dll function
        extern "C" _declspec(dllexport) void _stdcall ModifyString(LPWSTR str) {
             str[0] = L'X';
        }

        //Test.cs: Csharp function:
        [DllImport(“Native.dll”, CharSet=CharSet.Unicode)]
        public static extern void ModifyString(string str);

        public static void Main() {
           string str = “Hello, World”;
           ModifyString(str);
           Console.WriteLine(str);        
        }

    这里的关键是Unicode,.NET String的内部编码是UCS2,如果Native函数参数恰好用的是UCS2,出于性能考虑,CLR会直接把string内部字符串的地址传给Native;如果Native函数接受ASCII编码,CLR就不得不做一个拷贝了。
  • Hash算法从来都不保证不同的值生成的Hash值不同!
    正是因为散列函数可能出现冲突,所以在散列查找之后还必须比较键值以确定查找的正确性,所以一旦加入Hashtable,对象的键值是不能改变的。下面的程序用CLR内部的InternTable来展示改变一个Intern字符串的结果:
      static unsafe void ModifyConst() {
          string str = "Hello";
          fixed(char* pstr = str) {
              pstr[0] = 'X';
          }
      }

      static void Main() {
          ModifyConst();
          StringBuilder sb = new StringBuilder("Hel");
          sb.Append("lo");
          string str = sb.ToString();
      
       Console.WriteLine(str);

          switch(str) {
          case "Xello":
              Console.WriteLine("string is Xello"); break;
          case "Hello":
              Console.WriteLine("string is Hello"); break;
          default:
              Console.WriteLine("Not Found"); break;
          }
      }

    不妨猜猜看程序的输出是什么。(Warning: Think at your own risk. Potential side effects include dry mouth, sleepless and mind blow). :)

posted on 2004-08-23 04:22:00 by qqchen  评论(17) 阅读(6956)

More On Strings: The Implementation

  毫无疑问:String是引用类型。另一方面,String和int (Int32), Single (float)一样都是.NET的Primitive类型,CLR完全了解String对象的内部构造,并且有内建的用于String操作的指令(ldstr)。String的源代码不是用.NET语言/C#实现的,而是在CLR代码当中。在SSCLI的代码中Object.H里面可以找到下面的结构:
               class StringObject : public Object
               {
                  private:
                            DWORD   m_ArrayLength;
                            DWORD   m_StringLength;
                            WCHAR   m_Characters[0];
                 ...
                };

    另外,.NET程序仍然有办法打破String的Immutable——当然通常不建议这样做:

  • unsafe code: C#可以使用unsafe代码直接编辑一个String的字符序列:
               fixed (char* p = str) {
                     p[2] = 'X';
               }
  • P/Invoke: 传递String作为变量给Native函数的时候,Native函数可以改变String的值。

             

posted on 2004-08-21 05:24:00 by qqchen  评论(10) 阅读(6312)

Why are strings immutable?

    一直都想当然的接受了“strings are immutable”的事实,倒是没有仔细深入地想过原因。Google了一下,也没有找到满意的答案。我觉得大概有下面这些原因:

  •  避免字符串拷贝:如果String的内容可以改变,那么多个对象最好不要保存同一个字符串的引用,否则其中一个改变了String的内容就可能造成程序错误。这在多线程的环境下尤其重要,如果String不是immutable,那么它的所有编辑成员函数(Append, UpperCase等等)都必须要保证县城安全,性能损失惨重。如果每个对象保存String的一份Copy,则会消耗大量内存。Immutable实际上是一种近似于Copy-On-Write的折衷实现。
  • 维护集合语义:String是最常用来作为集合(Map,Hashtable) 键值得类型,而一旦一个String对象被用作集合的键值,改变String的内容就会破坏集合的语义,造成程序错误。
  • String Interning: 几乎没有人在程序中显式调用过String.Intern方法,但是Interning可能是.NET对String做的最重要的优化。简单的说,CLR为系统中的所有常量字符串维护了一张Hash表,所谓Interning就是在这个Hash表中找到一个动态生成的字符串的等值对象。通常比较两个String是否相等我们需要逐个字符的比较,但是如果两个String都经过Interning处理,因为Hashtable中的String都是唯一的,我们只需要比较这两个String的引用是否相等(是否指向同一个对象)。下面是String.Intern的一个简单的例子:
            bool StringEquals(string a, string b) {
                    string ia = string.Intern(a);
                    string ib = string.Intern(b);
                    return Object.ReferenceEquals(ia, ib);
            }

     C#编译器在两个地方隐含的使用了String Interning: 1) 如果在源代码中多次出现同样的字符串,只有一个对应的String会被放在CLR的StringPool当中,这个String对象会在代码中多次引用到。2) C/C++不支持基于String的switch/case,但是C#支持,这就是通过Intern实现的:C#编译器先把switch对应的字符串进行Interning处理,然后和下面的case进行引用比较就可以了。
     显然,String的Intern语义依赖于其不可变性:如果系统Intern Pool中的字符串可能会被改变,CLR就不能隐式的重用这些对象。

 

posted on 2004-08-21 04:49:00 by qqchen  评论(11) 阅读(6784)

Google Special Search

无意中发现Google针对几个Term的特殊搜索,包括MicrosoftAppleBSDLinux

查找Microsoft相关内容的时候msdn,gotdotnet等等网站的Page Ranking会被提高,这样更容易找到相关的信息。以后查找东西的时候就不用加上MSDN或者site:msdn.microsoft.com做关键字了。:)

上帝说的,有问题,找GOOGLE !!

posted on 2004-08-04 11:55:00 by qqchen  评论(16) 阅读(3838)

Structured Storage

  为回答一个CSDN上的问题Google了半天,居然没能找到一个像样的C#访问Structure Storage的例子。没办法,自己动手丰衣足食了。:)

  用C#作Structure Storage最大的麻烦是无数的COM Interface和API调用,实在没有心情把一堆COM接口函数都写出来了,只写了用到的几个: StgOpenStorageEx、IPropertySetStorage::Open、IPropertyStorage::ReadMultiple,而且功能也不全,只是取得Document的Creating Application属性,但是可以解决上面的问题了。(下载,没有找到合适的上传空间,随手申请了一个Free的)

如何判断一个文档是否是office文档????

FROM MY REPLY:

Office文件都是使用结构化存储复合文件(Structured Storage, Compound File),可以从它们的文件属性里面取得创建的应用程序,例如,在文件属性对话框当中的Summary一页有定义Application Name,可以看到文件使用Excel或者Word创建的。

但是在dotNet程序读取这些信息比较麻烦:
1)需要声明StgOpenStorageEx函数,包括参数的结构和常量定义。
2) 需要声明IPropertySetStorage,IPropertyStorage和IEnumSTATPROPSTG COM接口。
3) 定义Summary Information property set的FMTID:F29F85E0-4FF9-1068-AB91-08002B27B3D9。
4)通过StgOpenStorageEx打开文件,取得IPropertySetStorage.
5)用ReadMultiple读取Property ID = 0x12 (Creating Application),如果名字是Excel或者Word,那么是Office文件。

posted on 2004-08-04 11:44:00 by qqchen  评论(18) 阅读(7937)

Powered by: Joycode.MVC引擎 0.5.2.0