それでも町は廻っている(女仆咖啡厅)绝赞

之前因为看过《外天楼》和《机器人的礼物》成了石黑粉,但长篇的《それでも町は廻っている》一直没能看下去,无论漫画还是TV版,90%的负面影响来自《女仆咖啡厅》这个该死的译名。除了主角打工所在的“女仆咖啡厅”外,整部作品更多是围绕着“小镇”和镇上的任务展开的各种感情戏(?)、喜剧、奇想剧等等。

这个奇葩程度直逼“神通小侦探”的译名不知道让多少人错过了这部牛逼作品,不管有什么和本地化相关的理由,都无法接受。

石黑擅长直接通过作品中的人物吐槽,甚至多部作品直接吐槽自己的吐槽役角色。剧情上则擅长用平常的笔法描写怪异得不得了的事件——却从不让剧中人物察觉到所谓“真相”,以及让极其诡异、罕见的设定变得似乎是日常生活一般。从《外天楼》就能看出来,石黑是个重度推理作品爱好者。《それ町》的主角则直接设定成了超-重度推理爱好者,从第三卷开始我甚至觉得推理因素存在的话数占了多数,很多时候直接用了推理相关的neta。当然,《それ町》是日常系作品,推理也多以日常推理展开,“小题大做”自然也有不少,但有趣程度让人觉得有些桥段不改变小说实在浪费。

有些地方则讨论了各种奇怪的话题,比如绀出场的场面中提到了Cymric无尾猫,传说这种猫是因为上诺亚方舟时候尾巴被夹断了。女主提出了如下问题:

soremachi_cymric

相信很多人都会想过类似这种如果每一代鸡都砍掉两只脚若干代小鸡会不会没脚?问题吧,石黑让读者就会从头到尾感受到既视感、似曾相识、“啊啊我也这么想过”这样的感觉。

动画版也是特色突出的作品,如果不说是新房监督,恐怕这部没有什么新房突出风格的TV番组不会被多少人认为是他的作品吧。值得一提的是还原度实在太高了,现在觉得动画漫画只要看过一版就足够了。

反射和泛型delegate引起的Windows Phone App奇怪异常

昨天因为一个bug拖到晚上1点才睡——还没解决。今天又逐行debug才发现问题所在,这个因为反射的引起的crash实在是太熬人了,记录一下或许能帮到其他人。

先说一下场景和上下文。之前Windows 8项目中需要用到一个解析数据到同一个基类派生出来类型的聚合类,其中有大量的解析函数,根据期望类型调用不同的parser。如果用if else写会很挫,于是我用了dictionary将type和function对应起来:

private Dictionary> _parseFunctions;

增加方法时候,由于懒得一个个typeof硬编码类型,于是用反射这样映射进去:

        protected void AddParseFunction(Func func) where T : BaseResult {
            _parseFunctions.Add(func.GetType().GetMethod("Invoke").ReturnType, func);
        }

除了GetMethod硬编码之外,用的时候十分轻松愉快。现在想在Windows Phone平台上实现相同的效果,结果上来就发现Func不好用:.net 4.5版本支持参数协变和返回值反变:

public delegate TResult Func(
	T arg
)

但3.5版本不支持。所以需要改写成delegate:

public delegate T ParserFunctionDelegate( string inputString ) where T : BaseResult;

代码虽然是WP7.1的,但在WP8上跑得顺畅——注意问题来了,这段代码可以在WP7.1上编译通过——之前因为想到反正要做两个平台兼容的App,所以一直用WP8模拟器/设备测试。昨晚在WP7平台上调试发现坏菜,启动后就各种异常出现,而且直接跑到UnhandledException,却没有任何详细异常信息和调用栈,最后出错地点只能看到是调用封装类的xaml.cs或者.cs里面。碰到的错误都是“Cannot load type xxx from assembly yyy”,具体异常出现过:

  • InvalidOperationException
  • MethodNotFoundException
  • TargetInvocationException
  • ……

根据具体调用地点出现了乱七八糟各种平时没用过的异常。但基本上一眼看去就可以确定是反射相关问题,可惜我没顺着相关调用代码一点点debug,而是傻逼呵呵地继续怀疑是UI层或者工程重组时候GAC缓存出错,这个上面浪费了大量时间,也是为什么到半夜才睡的原因。

那么问题在哪呢?首先不是泛型delegate本身的错,因为我直接用上面GetMethod代码去拿ReturnType没有引发异常。那么肯定是协变的问题,去掉了delegate的out关键字后……先是提示类型错误,因为dictionary里面存储的是基类类型delegate,但我们传进去的是where限定的子类类型delegate,去掉了协变支持后,返回值便没法存入dictionary中。由此导致AddParseFunction不能接受泛型delegate,添加进去的parser functions也必须返回BaseResult类型而非子类型了,由此函数只有增加一个type参数(这也是最开始要避免的)手动传进来,没法再用反射拿到类型。

这样修改完成后终于搞定了棘手的crash,但代码变得异常寒碜。要说代码必须写得烂一些还能忍,那么这个完全无法从crash调用栈上获取任何信息的问题就不能忍了。如果是因为协变参数出问题,你好歹定位到相应错误行上呢,要么干脆就编译不过,省得个大圈子修改错误。