Know What, Know Where, Know How

今天发现一个很有趣的小细节,就是for loop在vb.net和c#是不完全等价的。

参考以下代码

— c#

int c = 0, x = 0;

for (int i = 0; i < 50 – x; i++)
{
x++;
c++;
}

— vb.net

Dim count As Integer = 0
Dim x As Integer = 0

For i As Integer = 0 To 50 – x
x += 1
count += 1
Next

在c#中只是循环了25次,vb.net则循环了51次。

大家能体会到个中的区别吗?

WAYS TO CLEAR COMBOBOX

Winform里面的ComboBox确实有很多tricky的地方。其中一个普遍认为的bug,M$在其support网站上给出解决方案。

BUG: ComboBox does not clear when you set SelectedIndex to -1

里面说的问题是,在databound模式下的ComboBox,如果当前选项不是第一项,要想清除它,使ComboBox什么都不选的话,需要把SelectedIndex设置成-1两次。这个是.net 1.0开始就有的bug,我在VS2005下能够重现,不过今天在VS2008 + .net 2.0下,好像问题消失了。

其实除了设置SelectedIndex外,还可以把SelectedItem设置成null。

另外,还看到有些程序员直接把Text设置成empty就算了。对于这种办法,我发现了一个新bug。

要重现这个bug不难。首先,ComboBox是否databound无所谓。在ClearForm的时候把Text设置“”, 看上去选项是清空了,但如果用代码(而非通过UI)选择一个新选项时,ComboBox还是空的,无论用SelectedIndex还是SelectedItem都无法把想要的选项选上。

通过debug,发现在设置ComboBox1.Text = “”后,SelectedIndex和SelectedItem根本没有清空,还是保留原值。当用代码给这两个值其中之一赋值后,另一个值会对应着改变,但是Text还是空的。

解决的办法有两种:

  1. 不用Text=””这种方法清空ComboBox,而用SelectedIndex=-1 或者 SelectedItem = null
  2. 如果一定要设置Text=””,那么在选择新选项时也要用Text来选,例如Text=”新选项”

后来,跟朋友谈了一下,他指出问题的原因是这个Text=””,因为””并不是ComboBox的选项之一,如果把””也加入到选项中,就不会出现这种问题。由此引申出,如果把Text设置成任何非有效选项,那么再想用SelectedIndex来选就会失败。所以,对于ComboBox,还是用SelectedIndex比较保险。

Programming & Mathematics

今日我标题党了一番,起了一个很大的标题。最近看了一个关于F#的presentation,突然对编程语言的背景知识产生了兴趣,于是wiki了一下相关内容。岂料这些相关的知识无穷无尽,最后上溯到数学这个“源头”。就这样,我白白浪费了一个星期的时间去看科普知识。这里,要慨叹一下维基百科的强大和伟大。

首先我想了解的是M$为什么要引入F#。原来F#是属于一种叫functional programming的programming paradigm,对应地,有我们熟知的Object-Oriented programming。在OOP大行其道了十多年后,functional programming又重新引起人们的兴趣(人总是喜欢怀旧)。M$的嗅觉一如既往地敏锐,于是在完善C#的同时引入了F#。

其实,所谓编程范型有很多,大致可分为以下几种:

  • Imperative programming
    • Procedural programming
    • Object-Oriented programming
  • Declarative programming
    • Functional programming
    • Logical programming

命令式编程最早是以面向过程为主,当时将一个个subroutine写成(封装成)function,通过一个主函数来调用。每一行代码就是编程者给电脑的一个“指令”,故而有“命令式”编程这个名字。程序通过修改程序状态(state)、程序内的变量来计算结果。后来人们发现,用函数来表示世界有点略显不足,毕竟一个“函数”体现的只是一种“行为”,缺少了行为的载体 — 对象。于是,人们想出了面向对象编程,把数据和函数都封装成对象,不再使用全局变量,而是用对象的属性来传递信息,而函数则变成对象的行为。

封装成对象后,程序变得更容易管理,进一步体现了模块化的理念。但本质上,命令式编程还是要求程序员告诉电脑how to do。而声明式编程的主要动机则是要程序员告诉电脑what to do,然后让电脑自己完成任务。函数式编程和逻辑式编程就归为这类编程范型。对于逻辑式编程其实大家并不陌生,SQL就是其中一种。而函数式编程也不是什么新鲜事儿,早在上世纪90年代就出现了纯函数式语言Haskell,另外我们熟悉的XSLT也算其中一员。

其实,现代编程语言一般都会支持多种编程范型的,例如C# 4.0引入了Linq本身就有很多函数式语言的元素(e.g. λ表达式)。而在过去追求pure的时代,纯函数式编程是不容许有side-effects(即在函数内部修改外部变量或系统状态)的,而构造式编程(即state changes只能发生在过程内部或者只修改输入参数或返回值)则不允许使用go to。

大部分程序员起初都是通过学习面向过程的语言入门,进而通过学习C++、Java掌握了OOP。对函数式编程熟悉的人估计不多。所以当M$引入F#时,大家都很感兴趣。其实函数式编程的理论基础是计算数学里的λ calculus。λ演算为函数表示和求值提供了一个理论框架。它的出现是为了解决“可计算问题”(计算数学的另一个问题是“计算复杂度问题”)。如果能证明一个问题(通过有限步)“可计算”,我们就可以用计算机来解决。而可计算问题更为人知的名字是“判定性问题”,在计算机领域就是“停机问题”。在函数式编程中有大量基本概念,例如fisrt-class function(函数A可以像对象一样作为参数传递给函数B),higher-order function(上述函数B),closure(javascript就很好地支持闭包),recursion等等。

与命令式编程不同,函数式编程更加贴近数学。虽然二者都有”函数”,但对于数学的函数,同样的输入值总会得到同一结果,而命令式编程的函数则不能保证这点。原因在于这些函数可以接受外部输入(如键盘输入不同值),访问并修改外部变量等。而函数式编程就是要反映数学函数的特性 — referential transparency。这样,函数的运行结果可预测,一个表达式与它的值可以相互替换。

在wiki阅读有关词条的时候遇到大量相关概念。很多概念最后都指向一个领域,就是数学。我是一个喜欢寻根究底的人。于是在此期间,我就顺便读了很多数学的词条,例如三次数学危机,康托尔集合论,罗素悖论,数学三大流派,欧式空间与非欧几何,逻辑演算等等,很多大学学到的知识纷纷浮现出来。可惜现在也就只能叹息一下,如果当初学数学的时候有先阅读一下历史背景,或者当时就会投入更大的热情去钻研,也不至于荒废了读大学的四年光景。

现在,我算是真正“计算机科学是数学的一门分支”这句话了。

很多时候我们需要用逻辑来选择form中的某个control。一般情况下,我们会调用Control.Focus()来完成。不过Focus()函数会先检查Control.CanFocus属性,只有True时才会执行,否则就什么都不干。
 
那么CanFocus要返回True,要满足哪些条件呢?
  1. Control的Handle已经建立
  2. Control是visible的
  3. Control是enabled的
第一、三个条件很容易满足,但第二个条件则不然。尤其是在Form.Load这个事件过程中,form的所有控件都未显示出来,所以此时调用某个控件的Focus()并没有效果。
 
对于要在Form.Load eventhandler中设置默认focus,可以用以下方法:
 
  • 调用Control.Select(),这个函数与Focus()类似,不过不会事先检查CanFocus,所以即使visible是false都可以select。当然了,Control.Selectable为True还是需要的
  • 在调用Focus之前,手动设置Form.Visible = true
  • 将那个需要默认focus的控件赋值给Form.ActiveControl,这样当form第一次打开的时候,就会自动focus到该控件
在这里,Control.Select()的适用范围是最广的,当Focus()不好使的时候,不妨试试Select()。
链条三:建安公司入伙避税与提现
开发商与炒房团的合作还有一个更高的等级,就是在建筑安装(下称“建安”)成本领域,帮助房地产开发商规避高额税收。
中国房地产行业的相关税收是非常高的,土地增值税是增值部分的30%~60%,所得税是25%。要是走正规手续,怎么算税收也在50%以上。因此对于房地产开发商而言,避税成了问题的关键。
建筑公司的意义即在于此,因为在房屋开发过程中,只有建安成本可以在增值税中扣除,其他的销售费用、管理费用、财务费用等等都不能在土地增值税的税前扣除,只能在所得税的税前扣除。另外,建安的营业税也是非常低的,只有3%,加上附加费用也就是3.3%,比普通5.5%的营业税比例要低不少。建筑承包最后的收益所得税一般是核定的定税,比例也很低。
操作链条是这样的。炒房团的操盘手会与开发商签署一个工程承包合同,开发商以建安费用把垫付的首付收回来,在开发施工中转包一手。
尤其是有装修的精装房更容易做高建安成本,因为施工质量不同,价格可以有天壤之别。笔者所知的有每平方米不足2万元售价的房子,开发商的装修标准做到了惊人的8000元。通过这样的操作,开发商可以直接享受税收上的好处,因为土地增值税税率是从30%至60%累进的,在建安成本大增以后,原来按照百分之五六十征税的增值税部分就变成按照百分之三四十进行征税了。
虽然建安公司也要有利益(其中包括1%~3%的承包管理费),但是经过这一腾挪,从建安公司得到的钱是把开发商的企业账户和支票按照承包费用的合法途径变成了个人账户的现金。
在中国,资金从企业账户到个人账户是一个大问题,对于类似炒房这样动辄超过百万的现金流动,央行反洗钱部门也是筛查监管的,而经过上述操作,不但解决了这一问题,而且使得政府在各种统计之中看不到。而给建安公司1%~3%的管理费,就等于是一个支票提现费用,在中国的商业环境下,这相当于把支票兑成现金而支付给钱庄的费用。
 
炒房新模式与海外热钱
在国家政策不断变化的背景下,炒房团的操作形式也在与政府政策博弈的过程中不断地发展变化。
民间高利贷的参与是炒房团的新形式。这样,炒房和给开发商提供资金二者就结合起来了。向开发商放贷的高利贷者要求开发商以打折扣的房子还贷,炒房团则在开发商没有正式销售许可来套取银行资金的时候为开发商提供资金,双方的结合使得社会游资进入炒房领域。
给开发商放贷,需要安全而且受到法律保护,如果崩盘清偿,放贷方能够优于银行等机构拿到款项。要实现这一点,还是需要借助建筑公司,因为按照我国法律规定的解释,建筑款项的清偿顺序是优于银行抵押的。
许多人知道,建筑公司给开发商垫资是一个潜规则,但一般理解的是“软垫资”,而不是“硬垫资”。所谓软垫资,是靠建筑公司拖欠民工工资和材料商货款来实现,但这种软垫资是很难跨年的,因为建筑公司要给民工和材料商结账,而房地产楼盘的开发建设却难以在一年内完成,尤其是北方冬天的好几个月都会因上冻而难以施工。这样,开发商和建筑公司还是有资金缺口,这就需要高利贷的介入了。
高利贷介入的途径,就是实现这些资金真正的“硬垫资”,归还高利贷则是以房子顶替工程款。
如此一来,这样高利贷资金进去,再以建筑费用出来,还可以让银行看到了开发商的“自有资金”,使得开发商能够合乎政策地取得银行的开发贷款。尤其是在开发商自己是“空手道”的情况下,更需要此类高利贷资金的“硬垫资”式参与。
一言以蔽之,这类新模式就是炒房团在前期参与房地产的开发,其提供的是相当于三四成房价的前期资金,得到的是一个重要的回报条件——在购买房子时不用付房款,余款等到房屋卖出之后再与开发商进行结算。
因此,在政府限制二套房贷款、套取银行资金的渠道受限以后,炒房团也可以新方式炒房了。这样的开发商与炒房团利益上的各得其所,是新时代的新发展。
这里还有一个关键的因素,就是炒房团赚取钱财以后,尤其是操盘手赚取了巨额财富以后,这些钱基本上都要逃往国外进行洗钱。但是同时,由于人民币升值压力以及资产市场的吸引等等,很多境外热钱试图进入国内,这样就产生了一个新的链条——与各种对冲基金运作类似的内外货币对冲,即国内的人民币炒房利润直接在国内以现金形式给了海外热钱的所有者,而海外热钱直接把美元转到炒房者的海外帐户。
这种地下交易由于不涉及银行国际清算系统的进出,外汇监管部门根本无从发现。悄无声息地入境以后,热钱也有很多参与到房地产市场的炒作,而热钱与炒房团的合作也使得大额的热钱资金隐匿于众多的炒房人群中难以被发现和监管。
据笔者了解,在限制外籍人士买房的政策出台以后,由于海外热钱的房产投资需求,这样的资产置换型互惠操作更加流行。沿海大量地下钱庄充当了这种资产置换的中介。如此这般,热钱流入国内,炒房利润也出境洗白。炒房者还以这样境外的钱为自己办理海外移民身份,再以外商的身份投资回到国内。
 
炒房团怎样组织运作?
炒房团找目标开发商炒房一般是不从售楼处下手的。去找销售经理、总监也不成,甚至总经理也不成,而是直接找到老板。这是因为知道的人越少越好,否则很多关系户都会找上门,按炒房团的价格要房子。
对于不熟悉的开发商老板,炒房团会从售楼处职员开始找,但是背后的运作机制是绝对不和职员讲的,一定要见到老板才会说。
对职员的说法就是以要大量团购为由要求见到老板洽谈。炒房团一般是找专门的房虫,他们的职业是倒卖项目赚取佣金,是一群资金掮客。在炒房的利益之下,很多房虫后来还成为了专业的炒房团中介。
最初,炒房团操盘手往往要说服开发商合作,但尝到甜头以后,开发商就会主动寻求合作,并且开发商的老板之间也会互相介绍。其表象就是某个炒房群体会专门跟在某个开发商的身后买房子,开发商到哪里就买到哪里,炒到哪里。
在炒房的利润分配上,如果是炒房团出资,操盘手团队至少分三成利润;如果是私募带资并操盘,炒房团的“演员”们可以分三成利润。为了能保证得到利润分成,操盘手团队会以建筑公司的建安费用作为转钱通道,参与的建安公司也可以有经手金额1%~3%的管理费。
 
炒房资金已经足以影响市场
经过多年的积累,炒房团积累了很大的利润,其足迹也已经遍布全国。
笔者在和房地产业界高端人士的沟通中了解到,目前热点地区50%以上的楼盘都活跃着炒房团的身影,全国的平均数字约为三分之一。炒房团参与的楼盘一般是购买控制15%到30%的房子,所以保守估计,炒房团在楼市中的市场份额应当在5%~10%,这也意味着市场上存在3000亿元的炒楼操盘资金。
2009年全国住宅销售均价上涨了约1000元/平方米,涨幅约为三分之一,炒房团关注的热点地区和豪宅涨幅更高,涨幅一般在一倍左右。因此以3000亿元的总量粗略计算,炒房团能够得到的利润应当在1000亿~1500亿元。考虑到炒房团近10年的积累(尤其是2002年以来的大展拳脚),目前积累已经达到数千亿元的规模。
这些游资更多以现金的形式存在并分散到个人,因此难以监管控制,但他们的行动却有严密的组织。 2009年底中国市场流通货币M0为3.8万亿元,现金游资已经在M0中占有巨大的比例,即使对于M1也是大约5%的水平,足以影响整个市场。此外,2009年全年房地产开发投资3.6万亿元,全年土地出让总价款1.59万亿元,巨额游资已经是房地产市场中不可忽视的力量。
这样的资本力量不受控制,房地产的问题就不是政策上的简单调控可以解决的。在市场经济中,资本就是决定性的力量。
炒房游资是投机的,但也是政策催生的,这样的情况需要深思。政策制定者需要做的不是禁止而是疏导。合理的政策应当堵死炒房资金确定的利润空间,这一目标可以通过信贷政策,即信贷管制的放松和缓解融资缺口来达到。
从房地产价格以及房地产开发过程来说,土地成本不可少、建安成本不可少、政府税收不可少,房价可以下降的部分,其实就是炒房团的利润,这些利润是可以也应该出让给购房者老百姓的。
 
开发商需要炒房团
办理假按揭套取银行贷款,这曾经是过去多年房地产开发商解决资金压力的惯常做法,但风险极大,而且监管也越来越严。尤其是限购二套房的贷款政策出台以后,找愿意假按揭的人也越来越不易。
但开发商的资金需求依然存在,而且在宏观紧缩的背景下变得更加紧迫,这就给炒房团留下了空间。
实际上,炒房团也是在套取银行资金,但开发商对此没有任何法律责任,都是由炒房团承担。而且,炒房团也是每一个成员独自面对银行和律师的审查,走的是正规渠道,也没有给银行和律师行贿,在形式上符合法律政策,银行工作人员也没有假按揭那样的责任问题。
很多人会问,开发商需要资金,为什么非常希望和炒房团合作,而不是直接先期降价卖给公众?这里有几个复杂的原因。
其一,部分房源的降价出售会令市场不容易接受后面的再涨价。尽管政府要求开发商不得捂盘、不得惜售,但是开发商只想低价出售部分房子以得到所需的资金,而其他房子是要高价出售的。因此开发商绝对不会接受低定价导致房屋被一抢而空的结果。更何况有的地方政府还制定了开发商不得中途涨价等规定。
其二,实际上最主要的原因在于:即便低价公开卖出(部分)房,开发商也不能迅速收到卖方所得资金。
地产业界的读者会很清楚,非地产业界的读者也很容易想清楚,只要你的开盘价比较低,很多人就会“打招呼”找到开发商“留房”。 房地产的开发过程中要盖上百个章,每一个拿章的人都会有类似的要求,要求留一两套房子。
打了招呼就不能再出售了,但是他们不会立刻给开发商房款,而是要等到房价涨了很多后卖出,再支付房款。
这样的结果不算是索贿受贿,因为成交价就是开盘价,是“市场价”成交,赚的钱是打招呼人可以解释的“投资房产”的收入。这种投资是旱涝保收的,房价没有涨或者跌了,他再说一声“不要了”,风险就被转嫁给了开发商。更有甚者,在房价上涨以后,还会有大量的人找各种关系,要求开发商以开盘价把房子卖给他。
打招呼要求留房的人实际上也有一个底线,就是不会主动要求开发商按照低于开盘价的价格(起码不是低很多的价格)卖给他房子,因为那样会有索贿受贿的嫌疑,不是“正常投资房产”。
正因此,开发商是绝对不敢以低价开盘,而引发这些打招呼要房、留房人的想法的。
正因此,开发商会希望和炒房团合作——定一个较高的开盘价,但立即以很大的折扣卖给炒房团(有时甚至是五折、六折),而且只卖出开发商希望卖出的数量,其他高价开盘的房子就慢慢地卖。
如此合作的直接结果是高开盘价、开盘高成交量,这会直接影响市场心态,给开发商和炒房团带来更大的利益。
 
链条一:炒房团哪来的钱?
炒房团的资金来源主要分两块——初始资金和银行信贷资金。
实际上,炒房团的主角们并不是真正的初始资金出资方,或者说只是小部分资金的提供方。更多的是私募带资管理和操盘手团队操作,而炒房团的成员只是被组织起来的“演员”。这些“演员”的主要任务一是前台露面;二是以他们的身份套取银行资金。
这些“演员”主要来自富裕地区的农村,操作者和村委会联合起来组织村民,并像传销一样洗脑,把他们包装成为有钱人,培训他们的言行以应对银行等部门的审查。
套取银行资金是关键。炒房团要获得足够的银行贷款,首先要让银行认为这些人是有钱人,能够有足够的资金去还贷。因此这些人就被包装成为有钱人,穿上各种名牌,还要编好发家故事,市场上流传的“浙江故事”、“山西故事”就来源于此。
这样的炒房团的组织结构一般以村庄为单位,因此这些人彼此紧密团结,村委会等出具证明、文件也更方便。
炒房团初始资金是用来提供贷款首付的过账资金。这部分初始资金不需要很大,因为可以一个人一个人过账。
对于炒房团而言,由于其获得的实际价格只是开发商公开售价的五六成,因此在得到银行按揭信贷之后,炒房团实际上连首付都不用以自有资金支付,而月供资金也可以从银行套取。
 
链条二:怎样撤出?退房!
炒房行为最终成功的关键是房屋要能够出手,顺利退出才意味着真正的成功。
从我国的政策,尤其是税收政策来看,炒房行为的成本看似是很高的,尤其是不足两年的房子。而且炒房团人数众多,又是异地炒房,对于有关房屋的管理也很难,抛售会很困难。那么炒房团如何退出?
这就要依靠开发商进行协助——退房。
一般来讲,炒房团都会和开发商签有退房协议,他们随时可以退房,开发商也可以随时将房屋再出售。采取这一办法最大的好处就是大大规避了税收,因为退房不需要交任何税款。由于是以开盘价的低折扣价格买入,退房意味着炒房团可以直接锁定利润。
这样的退房操作一般是在期房交房、开发商撤场之前。因此,实际上,炒房团持有房屋的时间一般也就这么长,往往不超过一年半,而收益一般是房价的30%。
 
房屋真正的销售都是通过开发商的销售团队或者专业的房屋销售公司来解决。此时炒房团拥有的房子和开发商还留有的房子就会一起卖,进行混合销售。这样,真正的买房人根本不知道自己所购买的是炒房团的房子还是开发商的房子,挑中了一套炒房团的房子,炒房团就退这一套,在购房者眼中是与直接购买开发商的房子没有区别的。这样的结果就是退房率超高,北京出现不少楼盘显示的退房率居然超过100%,也就是所有出售的房子被退过一遍,这背后就是炒房团的功劳了。
炒房团的每一个环节都经过了专业人员的实战演练,绝不是书斋中专家的纸上谈兵。
 
我看了下面的报道后,顿时无语了。心里很多想法,不知大家如何看待呢?还是大家都早已知道了,见惯不怪了,只是我后知后觉呢?报道是3月时出来的,当时新国十条还没有出台,我也只是偶然翻翻老新闻看到的,转一下。
 
温州富商、山西煤老板,这一批批“炒房团”,被认为是中国房地产市场的一股重要力量,参与了高额利润的分配。
炒房团的力量到底有多大?他们是怎样参与房地产市场的利益分配,就是对房子简单的低买高卖吗?他们的组织形式、商业运作究竟怎样?
对于外人来说,这的确是一个难解之谜。《第一财经日报》特约请房地产行业知名律师张捷先生,阐述他理解的炒房团利益链条。张捷先生曾经担任北京房地局有关机构的律师,同时也是北京房地产界知名楼盘现代城、篮堡、金地等第一大股东的私人律师,在房地产行业顶端从业多年。
在张捷看来,炒房团的核心是幕后的操盘手,其最核心的运作机制,是在房地产开发商资金链条进展的背景下,以某种方式为房地产开发商提供民间融资,赚取银行信贷利率与民间信贷利率的高额利差。
张捷先生阐释的运作逻辑,超出我们简单理解的炒房团运作模式。本报特刊登此文,呼唤更多的行业讨论。无论文中所谈到的现象具有多大的普遍性,都应该采取措施,从源头上杜绝此类投机运作方式,释放炒房团获取的巨额利益,真正给高房价下调创造空间。
[ 开发商主动勾结,利润实为银行信贷利率与民间利率之差,房价上涨是其次。几千亿的炒房游资已经成为地产市场的重要力量,消弭这部分利润才是最可行的治本之策 ]
只有从资金链条出发,才能真正看懂行业的秘密。
据笔者了解,属于游资性质的“炒房团”,其资金规模巨大。但市场对于其运作模式、盈利结构并不明晰。台前的角色往往只是借用过来充数的普通农民甚至挖煤工人。炒房团巨额资金的幕后操盘手,则脱离于公众的视线之外。
真正的富人是非常低调的,不会招摇过市,甚至不想让那么多人知道自己的家在哪里,他们都拥有自己的职业,并且非常繁忙,根本没有时间各个城市往来去炒卖和管理房子。
 
炒房的利润来源
几乎所有人都认为炒房团的利润来源于房价的上涨,但这一假设本身就直接低估了房地产开发商的智商——这意味着开发商的“贱卖”。
即便是在大牛市中,如果认为炒房团次次都能准确判断各个地区和楼盘价格的上涨下跌,也是太高估了炒房团的智商。
在这样的博弈中获取暴利并不容易,且风险巨大。更何况在计入税收因素后,这个游戏可能演变为零和博弈或负和博弈,也就等同于赌博了,这绝对不是大规模炒房团赚钱的常态。
炒房团真正的利益来源,是房地产开放商银行贷款利率与民间利率的利差。这是一笔确定的、高比率的收益。银行信贷利率约为年息7%,加上各类手续费不超过10%,但民间信贷利率一般是月息2%~5%,还要以复利计算。综合到年息,最高能超过100%。
在房地产整体信贷政策紧张的背景下,我国地产行业开发商实际上长期处于开发资金严重紧张的局面,因此往往不得不寻求民间信贷并忍受银行信贷和民间信贷的高利差。
在这个背景下,开发商是希望能够尽早出售房子并回笼资金的。因为即使是将来房价可以上涨一倍,其中的价差也是付了高利贷。高利贷的高额利息甚至还无法在税前扣除。
可以说,正是开发商高额的融资成本,才产生了期房与现房的差价,以及开发同样项目的一期与二期、三期的巨大差价。这种差价的本质不仅仅是房价的上涨,更是资金成本的反映。
对于开发商而言,炒房团实际上起到了资金来源的角色,炒房团所主要获取的确定的收益,也即是此利差部分。这是认识问题的关键之一,后面的内容是介绍如何实现这一点。
 

Why structs Immutable

很久很久没有写过blog啦,因为近半年来都在烦着人生的另外一件大事,所以工作方面真的有点儿偷懒,个人也没什么进步,真是有点愧疚。然而今天进来,发现一大堆junk comments,更显得烦心。Anyway,还是尽快写完这篇blog,然后回归工作台。
 
看了不少C#的教程都建议struct尽量immutable,虽然技术上没有任何限制,但还是把不可修改性作为定义struct的guideline。为什么呢?
 
其实,我当初也不理解,因为在C++里,struct和class没有本质区别。它们唯一不同就是,在默认情况下,struct的成员(method和field)是public的,而class则是private的。相信,很多人都和我一样,认为和class相比struct并没什么特别,有了class根本就不需要struct了,struct只是因为C++继承自C的缘故。可是到了C#,它们的区别却变得非常大。C#把struct划归为自定义值型(custom value type,对应reference type),所以struct是保存在stack上,而非heap里。struct被看为primitive type的扩展。
 
保存在stack上有什么问题呢?或者说struct被看作value type会带来什么特性呢?我们先来看一下整型,例如12,它本身是一个恒定的数字,在程序的生命周期中,我们不能用12来代表其它东西,如果需要其它数值,我们就需要一个新的数字,例如46。同样道理,如果我们需要定义一个struct来代表坐标平面上的点point(x, y),那么在定义了xy后,该点就应该定下来了。我们不应该直接修改这个点的xy,把它作为另外一个点来使用,而是需要重新定义一个点(从编程角度说,就是一个新实例)。所以,从某个角度讲,struct的每个实例都应是应“常数”。
 
文字叙述有点乱,还是看一下实际例子,
    1     struct Point
    2     {
    3         private int _x;
    4         private int _y;
    5 
    6         public int X
    7         { 
    8             get { return _x; } 
    9         }
   10         public int Y
   11         {
   12             get { return _y; }
   13         }
   14 
   15         public Point(int x, int y)
   16         {
   17             _x = x;
   18             _y = y;
   19         }
   20 
   21         public Point Move(int dx, int dy)
   22         {
   23             return new Point(_x + dx, _y + dy);
   24         }
   25     }

 

以上是一个immutable的struct的定义,当调用Move方法后,会返回一个新的实例,而不是直接修改本身的xy。可以联想一下DateTime的Add,还有String的Substring。我们在调用它们后都需要把结果赋值到一个新的变量中,原值并没改变。

讲了那么多,我都只在解释什么是immutable,却没有说为什么要immutable。既然技术上没有限制(struct可以定义public field,或者给property定义setter),那为什么不推荐这么做呢?原因是要减少编程中的逻辑混乱。考虑以下情况:

    1             Point p1, p2;
    2             p1 = new Point(1, 1);
    3             p2 = p1;
    4             p2.X = 2;

如果X和Y都可以随便修改,那当修改了p2.X后,p2和p1还相等吗?答案是否定的!

为什么?如果你把Point改成class,无论你怎么修改p1,p2的属性,它们都是永远相对的。可是,请记住,struct是value type,一旦将一个point赋值到另外一个point,这两个point就没有联系了。可以用整数的情况里理解,int i = j,之后修改了j,i是不会随之改变的。赋值是一种复制,但是对于value type和refer type,这个“复制”的意义却差别很大(参考我的另外一遍blog)。

因此,当我们定义struct的时候,应当禁止使用者直接修改其属性,这样才能保证逻辑的一致性。否则,情况会很糟糕,尤其是作为函数参数传递的时候,使用者很难判断是用ByVal还是ByRef。所以,我们务必将struct和class区别对待!

 

最后,我们来考虑一个比较有趣的问题。原则上,既然struct是value type,它的field和property也应该是value type的。但是,技术上并没有阻止我们定义一个ref type的属性。那么,如果修改这个ref type的属性的属性后,结果会怎样呢?(容许我懒一下,没有把以下vb代码转成C#)

    1     Structure Test
    2         Private _x As DataTable
    3         ReadOnly Property X() As DataTable
    4             Get
    5                 Return _x
    6             End Get
    7         End Property
    8 
    9         Public Sub New(ByVal dt As DataTable)
   10             _x = dt
   11         End Sub
   12     End Structure
 
    1         Dim dt As New DataTable("Table1")
    2         Dim s1 As New Test(dt)
    3         Dim s2 As Test = s1
    4 
    5         s2.X.TableName = "Table2"

你会发现 s1.X.TableName 和 s2.X.TableName 最终都等于"Table2"了,为什么?请仔细想想。

 
对于这种情况,我建议还是把这个struct改为class比较理想,即使不需要为它定义任何函数。这样就不会带来混乱了。

The Truth of ByVal & ByRef

说来惭愧,这个话题理应在中学时期就必须搞清楚,记得当时学C,老师就已经着重解释过形参,实参。不过到了今天,我才真正“完全地”理解了关于这两种参数传递方式的奥妙。看来知识,只有在实践中使用过,才能真正理解透彻。
 
其实,一般情况下对两者的理解是没有问题的。VB.NET默认是ByRef,而C#默认是ByVal(姑且引用一下VB的keyword来讲述C#)。
 
对于值类型变量,其值直接储存在stack中。
ByVal – 系统会在stack上复制这个值,然后再传给函数。表面上看,就像是“复制一个与原来变量相同的变量”。所以在函数里对这个copy的修改不会影响原变量。
ByRef – 系统会在stack上创建一个指向这个值的指针,然后把指针传给函数,这就等同于“把变量直接传给函数”。因此在函数里修改这个值,会反映到原变量。
 
对于引用类型变量,其“值”在stack中是一个指针,指向保存在heap中的对象(内容)。
ByVal – 与值类型相似,系统会在stack上复制一个“值”来传给函数,可是此时这个“值”是一个指针,其也指向heap的对象。所以,如果在函数里修改其指向的对象(heap的内容),原变量指向的对象的属性也会被修改,因为“它们指向的是同一个东西”。
ByRef – 同样与值类型相似,系统在stack上创建一个指向这个“值”的指针,也就是一个“指向指针”的指针,然后再传给函数。为了讨论方便,我把新建的“指向指针”的指针称为p1,而原指针称为p2。此时,情况看上去有点复杂。我们不但可以像ByVal那样修改heap里的对象,还可以修改p2(这是写这篇文章的原因)。
 
通常情况下,我们只需修改在heap上的“真实”对象的属性。例如,一个Employee对象,包含一个Name属性,我们想通过函数修改Name。此时,ByVal和ByRef并没有不同,两者都可以修改被传入的实例的Name属性。既然这样,为了减少复杂度,一般应优先选用ByVal。
 
以上内容都很好理解(晕,这可是中学课本的知识啊)。但,考虑一下一种很特殊的情况,当用ByVal传实例,然后在函数中把它赋值为null,那么原实例会变成null吗?答案是,不会!
 
原因很简单,你这样修改,并不是修改heap中的内容,而是直接把stack中的“值”(一个指针)改为指向null(null也是一个对象)。请记住,对于ByVal,这个“值”只是一个copy,原值还是指向那个heap中的对象,所以原实例不会变为null。(2月24日对这问题添加了一点补充,请转到文章结尾)
 
要解决上述问题,只能使用ByRef。这是我唯一想到的需要用ByRef来传输引用型参数的情况,也就是,你需要把原引用实例设为null,或者指向另外一个对象(通过new或赋值)。
 
 
String
最后,让我们看看String这个“特殊”的类型。说它特殊,是因为从使用上看,它有值类型的特点,又有引用类型的特点。其实,问题不在于此。String还是引用型。它的特殊性在于它指向的内容不能修改(immutable)。String的内容(字符串)是保存在系统在heap中开辟的一个特殊区域里,一个字符串就是一个对象。例如,
Dim s As String = "Hello"
Dim t As String = "Hello"
 
heap中只有一个"Hello"字符串对象,而s和t是stack中两个不同的指针,同时指向"Hello"对象。
 
那么,如果我们需要在函数中修改一个String实例,就需要用ByRef来传,这里“修改”实际上是指在heap中创建一个新的字符串,然后把原实例指向它。而用ByVal的话,表面上与值类型变量一样,函数里赋一个新的字符串值,并不会对原实例有任何影响。
 
 
[2月24日]补充
对于引用型变量有个很一般,但很容易被忽视的问题(课本上也有的,不过我当时就是忽视了)就是,修改其属性或是重新赋值,对这个变量的其它副本有什么影响。
 
先来看赋值,考虑以下两种情况
Dim i As Int32 = 0
Dim j As Int32 = i
 
i = 1
—————————–
Dim obj1 As New Class1
Dim obj2 As Class1 = obj1
 
obj1 = New Class1
 
无论是值类型还是引用型的变量,对于它的其中一个copy重新赋值的话,是不会影响其它copy的。
 
再来看属性修改,这个只能考虑引用型
Dim obj1 As New Class1
Dim obj2 As Class1 = obj1
 
obj1.Name = "Obj1"
obj2.Name = "Obj2"
 
当程序运行到倒数第二行是,两者的Name都会变成“Obj1”。而到了最后一行,Name又都变成“Obj2”。
 
由此看出,对于值型变量,各副本之间是没有关联的。对于引用型变量,各副本的关联在于heap中的实例(它们共享一个实例),而stack中的指针是没关联的,后者(stack)与值型变量一样。倘若重新赋值了,就完全脱钩了。
 
结论,在修改引用型变量的时候要考虑清楚各个副本之间的联系,有时候这种联系是必须的,有时候可能会影响你的预期结果。
昨天写了一篇关于用ASP.NET生成含有Unicode的CSV的文章。后来经理说,还是希望使用xls格式,而不是csv。于是,便苦思冥想如何搞定tab分隔的“文本文件”可以用Excel直接双击打开(Excel能直接识别tab就是分隔符)。终于,今天被我找到了答案,那就是把xls文件用UTF-16来输出,这样既可以正确显示Unicode而不是乱码,又能够处理好表格格式。因为上一篇文章已经分析了问题,这里就直接贴一下代码。
 

    1     string s = "보증인\t보증인"; // 使用tab分隔

    2 

    3     UnicodeEncoding utf16BOM = new UnicodeEncoding(false, true); // 指定encoding实例不使用BE,而允许输出BOM

    4     int byteCount = utf16BOM.GetByteCount(s);

    5 

    6     byte[] prefix = utf16BOM.GetPreamble(); // 取出UTF-16的BOM字节

    7     byte[] output = new byte[prefix.Length + byteCount]; // 声明输出的字节数组,注意长度

    8 

    9     System.Buffer.BlockCopy(prefix, 0, output, 0, prefix.Length); // 复制BOM

   10     utf16BOM.GetBytes(s.ToCharArray(), 0, s.Length, output, prefix.Length); // 写入正文

   11 

   12     Response.ContentType = "application/vnd.ms-excel";

   13     Response.BinaryWrite(output);

   14     Response.End();

刚刚发现,CSV是不能用UTF-16编码的(在Excel里逗号被视作普通字符,而不是分隔符)。我猜想,是因为UTF-16把所有ASCII字符都编码成双字节,在解码时那个“双字节逗号”就被认为是普通内容了。UTF-8编码的CSV没问题,是因为UTF-8使用一个字节来编码英文和数字符号,这部分是和ASCII完全相同的。

由此得出,输出CSV要用UTF-8,输出tab分隔的文件要用UTF-16。以后有机会应该看一下如何直接生成xlsx格式的文件,或者那时就不会这么麻烦。