linkcd 又一个 博客堂 站点 站点

26九/05

这不是bug, 这是设计.

"这不是bug,这是设计!"

               -- 某软件开发人员对用户说道.

这好像是个很老的笑话, 但我就刚遇到了一次.

原先遇到这个issue, 然后我当作bug报给了MS, 在9月13号ADO.NET的PM  Kawarjit Bedi 回复说这是个design而不是bug, 并给出了理由.

谢谢Kawarjit.

但没想通的是, 我明明注册了这个Issue的MSN Alert的, 但是没有收到任何信息, 于是9月13号的回复我到现在才看到, 真奇怪.

1九/05

.Net 2.0 下Container性能比较: Binary Serialize Dataset vs Custom Classes

我们已经听过太多应该是用 Dataset还是应该使用自定义Class作为container的争论了.

很多Anti-Dataseter 对于.Net 1.1 下面dataset蹩脚的xml序列化方式很是不屑. 不可否认, xml序列化的确是dataset的一大缺陷.  但是Microsoft已经意识到了这个问题, 在即将推出的.Net 2.0中,他们为dataset提供了真正的binary序列化方式.

那么, 改进后的binary dataset性能到底如何呢? 在2.0时代, 什么样的container才是我们正确的选择呢?

本文通过实际的测试结果告诉您 :)
请点击阅读全文.

18八/05

Undocumented Keywords in C#

研究Int32&的时候,无意中发现C#里面还有4个Undocument Keyword, 分别是__makeref, __reftype, __refvalue 以及__arglist。 其中前三个keyword可以这样用:

        int i = 1;
        
        TypedReference tr 
= __makeref(i);
        Type t 
= __reftype(tr); //t = System.Int32
        
        
int i1 = __refvalue(tr, int); //i1 = 1
        int i2 = (int)TypedReference.ToObject(tr); //i2 = 1
        
        i
++//i = 2
        
        
int i3 = __refvalue(tr, int); //i3 = 2

(关于TypedReference类型MSDN是这样描述的:Describes objects that contain both a managed pointer to a location and a runtime representation of the type that may be stored at that location. 同时TypedReference有
[CLSCompliant(false)]标记)

于是我们可以用下面这种方法来模拟ByRef的参数

public class MyClass
{
    
public static void Main()
    
{
        
int v = 99;
        TypedReference trParameters 
= __makeref(v);
        Foo(trParameters);

        Console.WriteLine(
"v = {0}", v); // v = 100 
    }

    

    
public static void Foo(TypedReference tr)
    
{
        
if(__reftype(tr) == typeof(int))
        
{
            __refvalue(tr, 
int)++;
        }

    }

}

比较不爽的就是我们必须在Foo方法体中判断TypedReference包含的类型。

注意如果把Foo写成public static void Foo(ref TypedReference tr),编译器会抱怨说:Method or delegate parameter cannot be of type 'ref System.TypedReference'。

至于__arglist则可以模拟params关键字,抄个例子:

public void Function(__arglist) 
{
    ArgIterator iterator 
= new ArgIterator(__arglist);
    
for (int count=iterator.GetRemainingCount(); count>0; count--)
    
{
        TypedReference typedRef 
= iterator.GetNextArg();
        Console.WriteLine(__refvalue(typedRef, 
int));
   }

}

调用它:Function(__arglist(2,3,4));  输出2,3,4

后注:这4个keyword毕竟是undocument的,微软也没有提供任何支持,所以不排除以后被delete掉。Mono下面的编译器似乎也不支持,不过我在vs2005 beta里面试验还是有效的。

 Updated: Flier Lu在他的blog对__arglist的使用作了更深入的探讨,推荐阅读。 :)

14八/05

System.Int32&是个啥?

话说有一天你要用反射来对Target类进行操作,调用Foo函数。
public class Target ...{ public void Foo(int x, int y, int z) ...{ } public void Foo(int x, ref int y, out int z) ...{ } }
可以看到Foo有2个重载,唯一的区别在于第二个Foo方法签名中有带ref的参数。
如果你直接写:

Target myTarget = new Target(); Type t = myTarget.GetType(); MethodInfo methodInfoWithRefParamters = t.GetMethod("Foo");
那么会在RunTime扔个Exception说: Ambiguous match found.
要想invoke第二个Foo的话,我们必须这样写:
MethodInfo methodInfoWithRefParamters ;
Type[] typeList = new Type[] {typeof(int), Type.GetType("System.Int32&"),Type.GetType("System.Int32&") };
methodInfoWithRefParamters = t.GetMethod("Foo", typeList );
好吧,现在运行是正常运行了,可是新的问题又来了:这个System.Int32&是个啥东东呢?
 
先在MSDN/.Net Reflector找找Int32&,没有。Google和查看微软放出的Framework Source Code,同样无果。
 
得,现成的资料找不到,那只有让我们自己通过做试验来玩玩了。
 
1。 先试试能不能搞一个Int32&的实例出来:

当然,就不用指望直接能在C#里面new一个Int32&了。(如果是unsafe的话,Int32*到还可以)
来试试亲爱的Activator吧:

Type refInt32Type = Type.GetType("System.Int32&"); object myRefInt = System.Activator.CreateInstance(refInt32Type, true);
哎呀,扔了一个“No parameterless constructor defined for this object.”异常出来。
 
2。好吧,那让我们来看看Int32&有啥样的constructor:
BindingFlags bf = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance
                              | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.NonPublic ;
Console.WriteLine(refInt32Type.GetConstructors(bf).Length);
结果却返回了0。
狠一点,把全部BindingFlags Enum放到bf上去呢,用GetMembers试试呢?
BindingFlags bf = // all values of BindingFlags Console.WriteLine("count of member in type Int32&: {0}", Type.GetType("System.Int32&").GetMembers(bf).Length);
oops,输出长度还是0!
 3。Ok,Ok,不能创建实例就暂时不管它,那让我们用QuickWatch来看看这个Int32&类型有啥属性:
下面是我把Int32,Int32&, Int32*,外加Boolean&类型做对比的结果。(属性值相同的已经略过)
 
 
通过这张表我们可以观察到很有意思的结果:
Int32& 是ByRef的(最明显,呵呵),不是ValueType, 不可以序列化,没有Sealed,GUID都是0,BaseType 显示为《undefined value》
 
请特别注意的是Int32&, Int32*它们IsClass属性为True,说明是个Class,先记下来,在后面我会给大家看一个更有趣的结果。
 
既然,Int32&和Int32*都说自己有ElementType,那就来看看是什么Type吧:
Type.GetType("System.Int32&").GetElementType(); // Print “System.Int32” Type.GetType("System.Int32*").GetElementType(); // Print “System.Int32” too.
4.回过头来看看生成的IL代码呢?
 
如图,只能看到对于ref/out 的参数,使用的是ldloca.s 指令,还是没看到我们的Int32&类型,
 
5.最后来看看最有趣的发现:
先试试在你的机器上run这3句代码:
Console.WriteLine(Type.GetType("System.Int32&").IsSubclassOf(typeof(object))); Console.WriteLine(Type.GetType("System.Int32*").IsSubclassOf(typeof(object))); Console.WriteLine(Type.GetType("System.Boolean*").IsSubclassOf(typeof(object)));
再看结果,呵呵,吃惊吧。以上3句都是输出False。难道说.Net里存在着不是从Object继承的Type吗?可它的IsClass属性是为True的哦~
会不会Int32&不是属于.Net框架的呢?可看看它的AssemblyQualifiedName是“System.Int32&, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”,也没看出什么问题啊?(汗)
 
个人初步猜测:
 
Int32&类型并不是一个真实存在的类型(原因是通过Reflector没能发现它的踪影),而是在CLR中动态构造的一个类型。MS使用它来“表示/标志”某个ValueType的地址。

2005/08/16 Update: 
思归的帖子,以及Joel Pobar的blog对这个问题作出了比较好的解释,有兴趣的读者可以看原文,偷懒的话就听我说一下吧
 
ByRef types 是受控指针,在CLR里面也是真实存在的类型。不过因为只有很少的IL指令能够作用与它,所以我们不能创建它的实例。同时,系统也限制我们只能在parameter的位置使用ByRef的类型。值得注意的是Int32&和Int32之间不存在什么关系哦...
 
 

21七/05

《4个程序员的一天》(续) 由idior的问题想到的

《4个程序员的一天》一文中,idior问道:

不知道类似foo(+,-,+,*,/, a) //a is a array
实现a[0]+a[1]+a[2]*a[3]/a[4]这种功能能实现吗?(用FP)

昨晚去看了看scheme的数据结构操作,得出以下解法:(;号后面是注释)
(define (inner-runner 1st-number operators numbers)
  (if (= 0 (length operators))
    1st-number
    (inner-runner ((car operators) 1st-number (car numbers))   ; calculate new 1st-number
                          (cdr operators)                                            ; get new operators list
                          (cdr numbers))))                                          ; get new numbers list

(define (Foo operators numbers)
  (inner-runner (car opes) ops (cdr opes)))

然后我们来run run试试:3 * 5 - 2 + 4  = 17
> (Foo (list * - + ) (list 3 5 2 4))
等于17 ,得解 ;)

好了,现在来说下解题的思路:
Setp1:从Numbers中“POP”出第一个数作为初始化操作数,叫做1st-number,。Numbers现在剩下n个数字,我们称这个新的list为Numbers*.

Setp2:然后递归的分别从Operators和Numbers*里“POP”出运算符和第二个运算数,拿来和前面我们拿到的1st-number做运算,把得到的结果当作新的1st-number,连同新的Operators和新的Numbers*,重复step2,直到Operators变为空。

这时候再回过头来看上面的scheme代码:

先简单说明下scheme的语法:
(list * - + ) :构造一个list,包含3个元素:*,-, +
(length operators):得到operators这个list的长度

(car operators):得到operators这个list的第一个元素。
例:(car (list 1 2 3)) = 1,注意它返回的是一个元素,不是list

(cdr operators):得到operators这个list从第二个元素开始的子list
例:(cdr (list 1 2 3)) = (list 2 3),注意它返回的是个子list而不是元素

来看看inner-runner函数,它完成的是step2的工作:
它先看看operators是否为空,如果为空就直接返回1st-number;如果不是,那么分别取得一个operator和一个number,进行(operator 1st-number number)运算,把得到的结果又当作1st-number,连同剩下的operators和numbers进行下一轮的递归。

至于主函数foo就比较简单了。它接受2个list类型的参数:Operators,包含n个运算符,Numbers,包含n+1个数字(当然你也可以放多于n+1的数字,不过多的数不会参与运算),Foo做的工作只是取出最初的1st-number,剩下的就交给inner-runner搞定了。

至于预算步骤,让我们来看看(先减少些运算符以缩小运算过程):
(Foo (list * - ) (list 3 5 2))  =>

(inner-runner
  (car (list 3 5 2))
  (list * -)
  (cdr (list 3 5 2))) =>

(inner-runner
  ((car (list * -))
   3
   (car (list 5 2)))
  (cdr (list * -))
  (cdr (list 5 2))) =>

(if (=
     0
     (length (list -)))
  15
  (inner-runner
    ((car (list -))
     15
     (car (list 2)))
    (cdr (list -))
    (cdr (list 2)))) => 等于13

怎么样,明白了吧? 事实上,scheme很多运算都是靠这种简单的递归来完成的。

如idior所说,这个问题可以在".net下利用delegate的多播可以方便实现, java利用compostion模式也能完成"。(如果idior你有时间的话,能否说明一下compostion的解法呢? 毕竟Design Pattern方面你比较熟,呵呵)

而且就表现方面来说,虽然scheme代码看上去比较少,不过对于大多数programmer来说,代码的可读性也是非常重要的。FP的代码咋看上去,的确很让人头晕...@_@

写到这里,我们的Semon同志过来看了看我这post的草稿,对我上面最后一句话很是不屑,然后提了一个问题:

既然已知3 * 5 - 2 + 4  = 17,那么用scheme这样写:(Foo (list * - + =) (list 3 5 2 4 17)),即在operators最后加入一个=号,在numbers最后加入结果17,该语句返回true。(Foo (list * - + =) (list 3 5 2 4 18)) 返回false。

你能用java或C#实现这个最后判等的功能么?

哇呀,这可把我难倒了...

各位看官,您有啥好建议么?

PS:很高兴来到博客堂大家庭,请大家多多指教 :P