转载原地址: http://www.cnblogs.com/JeffreyZhao/archive/2009/02/27/mvc-use-strong-type-everywhere.html

我们继续来谈《最佳实践》,这次的主题便是“强类型”。

一直说C#是强类型语言,通俗地讲,便是指C#中的“变量”在开发时的类型便是明确的:String便是String,Int32就是Int32,毫无争议。强类型的好处有很多,张嘴便可随意举上几例:

  • 能够享受代码提示功能
  • 能够获得重构工具的支持
  • 能够在编译期发现更多错误
  • ……

不过C#也不是“绝对”的强类型语言,因为它也有弱类型,那就是Object。我们知道Object是所有类型的最终基类,任何类型的对象都可以使用Object来引用。可是一旦转化成Object的变量之后,代码提示便消失了;即使我们“明确”对象的确切类型,也必须通过Cast才能使用——更何况它形成了一种被“滥用”或“误用”的机会。例如一段错误代码可能会传入一个不符合约定类型的对象,那么就会造成错误。更严重的是,这样的错误可能只要在“运行时”才能被发现,编译器对此无能为力。

类似的“实践”其实很多,例如“方法尽可能接受抽象的类型,而返回具体的类型”。

ASP.NET MVC中对于“强类型”一说最典型的方面便是视图。在ASP.NET MVC中每种视图(View,Partial,Layout)都可以选择弱类型和强类型两种“基类”,两者的区别便是Model的类型。强类型视图的唯一区别便是其Model属性为范型参数所指定的类型——而弱类型则自然就是Object了。在这里我提出一个“最佳实践”:总是使用强类型的视图,并且所有数据都从Model中获取。这么做可能会在一定程度上增加了代码量,因为我们需要为每个视图建立一个Model,不过我认为这是值得的。在强类型的视图中,VS中能够对Model的各种成员做出丰富的代码提示,我们便可以快速地输入代码,并确保不会出现“拼写”之类的低级错误。如果使用了ViewData这个弱类型的字典,那么每次我们获取某个值之后,都必须将其Cast成具体的类型才能使用,这无疑使视图模板变得复杂而难以维护。

其实以上的准则还有另一个层面的内容。使用项目模板安装了ASP.NET MVC之后,在~/Views/Shared/LogOnUserControl.ascx文件中可以发现对Page.User属性的直接访问。这自然可以运行,但是其带来的后果是“弱化”了aspx/ascx/master文件的“模板”概念,而重新让其意识到“我是一张页面”。在ASP.NET MVC框架中,我们要时刻记着“项目中没有页面”,我们始终使用Controller中的Action方法来处理请求,而每个请求需要对应一个磁盘上“物理文件”的思维方式一定需要改变(无论是在用WebForms模型还是MVC模型)。直接访问Page.User属性也使我们很难为视图进行独立的单元测试,因为视图与HttpContext直接耦合,而HttpContext的Mock相当困难。

在《最佳实践》的示例中,我为每个视图都构建了一个Model,它们都在MyMvcDemo.Web.UI.Models项目中。值得一提的是,由于业务的需要,视图之间很可能出现“数据共享”。例如View和Layout(更ASP.NET的说法是Page与Master Page)之间共享同一个Model。更进一步,两者之间其实是多对多的关系,如果我们为每个ViewPage定义了强类型的Model,又如何应对同一个ViewPage套用不同ViewMasterPage的情况呢?这个问题最自然的解决方式便是使用接口。一个Model对象不可以有多个父类,但是完全可以实现多个接口。因此,我们往往为强类型的ViewMasterPage指定一个接口作为其泛型参数。从示例中您也可以发现,Site.Master的类型为ViewMasterPage<ISiteMasterModel>,而每个ViewPage的Model类型都实现了ISiteMasterModel接口。

使用ViewData的另一个坏处是必须使用字符串作为键进行访问。字符串是什么?是常量。分散在各处的常量是维护性的大敌,而使用ViewData则几乎无可避免地将字符串常量分散在控制器和视图两个地方——您可能会觉得,使用枚举不就解决这个问题了吗?如果这么做,相当于为每个视图定义不同的枚举类型,那么我们为什么不直接构造强类型的Model呢?使用字符串作为键的另一个坏处是无法在编译期发现问题:如果您一不小心拼写错误了怎么办呢?您可能会觉得,原本aspx就必须到运行时才会动态编译,不过现在ASP.NET MVC的模板已经增加了相关的MSBuild Task,或者使用ASP.NET的预编译功能,都可以在编译时对视图进行检查了。这也是我建议大家使用aspx,而不是另一种DSL来构建视图的原因——aspx的周边支持实在是太丰富了。

不过在视图中,字符串常量并非只出现在ViewData的访问上。例如,使用ASP.NET MVC框架模板创建项目之后,Site.Master中生成导航栏链接的代码是这样的:

<ul id="menu">
<li><%= Html.ActionLink("Home", "Index", "Home")%></li>
<li><%= Html.ActionLink("About", "About", "Home")%></li>
</ul>

代码使用了Html.ActionLink这个辅助方法生成一个导向至某个Action的链接,只可惜这里又出现了字符串参数表示的Controller名和Action名。事实上,如果您下载了ASP.NET MVC RC的源代码之后,能够发现其中的MvcFutures项目中包含了“强类型”的ActionLink辅助方法,于是我们的代码就能修改为如下模样(参见《最佳实践》的示例):

<li><%= Html.ActionLink<HomeController>(c => c.Index(), "Home") %></li>
<li><%= Html.ActionLink<HomeController>(c => c.About(), "About") %></li>
<li><%= Html.ActionLink<AccountController>(c => c.Register(), "Register") %></li>
<li><%= Html.ActionLink<AccountController>(c => c.List(1), "Admin") %></li>

这下我们便可以使用Lambda Expression这个强类型的表示方法来“告诉”ActionLink方法究竟该生成导向至哪个Action方法的链接。甚至我们可以在“调用”Action方法的同时指定其参数——例如最后一行代码,指定将查看“第一页”内容,而这些数据在交给URL Routing的配置后便能得到我们想要的URL。MvcFutures中还定义了其他类似的辅助方法,例如UrlHelper中的辅助方法则会直接生成一个URL链接——而不是一个<a />元素。可惜目前MvcFutures中的这些辅助方法编写的并不完全“正确”,因为它直接把方法名作为Action的名称来使用,而ASP.NET MVC从某个预览版开始引入了ActionNameAttribute,可以为一个Action方法指定一个不同的Action名称。因此如果您使用ActionNameAttribute来改变Action名,则很可能您需要构建自己的辅助方法。不过这条准则依旧成立:使用强类型的表示法生成URL地址。

如果您要使用MvcFutures中的内容,则必须自行编译MvcFutures项目。有一点值得注意,如果您使用直接编译ASP.NET MVC RC解决方案所得到的Microsoft.Web.Mvc.dll,就会发现它依赖的是“System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”,而您开发时所使用的,也就是安装在系统中的程序集是“System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35”,两者不同自然难以兼容。幸运的是,您只要手动修改一下MvcFutures项目的程序集引用就可以了,对您来说这一定不是问题。

看到这里,不知道您是否发现了一个比较“严重”的问题,其严重程度大大降低了这些负责生成URL的辅助方法的可用性。这是个什么问题呢?又该如何解决呢?我在这里先卖个关子,下个星期我将公布这个问题,以及它的解决方法——这部分内容并没有包含在上次《最佳实践》的讲座中,算是一个“保留节目”吧。:)

C#中的强类型说明的更多相关文章

  1. 在.NET Core控制台应用程序中使用强类型配置

    想象一下,你写一个控制台应用程序,你想要从配置文件中以强类型方式读取配置. .NET Core 可以帮助我们解决. 通常我会在ASP.NET Core MVC中演示,但简单起见,只在控制台应用程序中演 ...

  2. 03-TypeScript中的强类型

    在js中不能定义类型,而是根据赋值后,js运行时推断类型.在ts中支持强类型,强类型包括string.number(浮点型,不是整型).boolean.any(任意类型).Array<T> ...

  3. 对于ASP.NET MVC中页面强类型的个人理解

    进入ASP.NET MVC学习 发现很多和winfrom不同的东西,但是利用的C#语言还是没有变化,更多的是利用了新的语言,html jquery ajax.....唉 心累,一本书一本书看的去 看完 ...

  4. C++11 中的强类型枚举

    // C++11之前的enum类型是继承C的,不温不火: // C++11对enum动刀了,加强了类型检查,推出强类型enum类型,眼前一亮 // 使用过QT 的都知道,早就应该这么做了,用的很爽!! ...

  5. c#中的强类型、弱类型和泛型

    强类型和弱类型的变量都有两个属性:类型和值. 强类型的变量类型是不能改变的,弱类型的变量类型是随需改变的,这是强弱的真正含义. 我们在编写c#代码时,变量类型是明确的,不可更改的,如string就是s ...

  6. 使用mvc时,在视图view中使用强类型视图,在web.config文件中添加命名空间namespace的引用不起作用,解决方法

    这是view中的model代码: @model t_user_info 这是web.config配置文件只的代码: <namespaces> <add namespace=" ...

  7. .NET Core 2.x中使用Named Options处理多个强类型配置实例

    来源: Using multiple instances of strongly-typed settings with named options in .NET Core 2.x 作者: Andr ...

  8. 解读ASP.NET 5 & MVC6系列(12):基于Lamda表达式的强类型Routing实现

    前面的深入理解Routing章节,我们讲到了在MVC中,除了使用默认的ASP.NET 5的路由注册方式,还可以使用基于Attribute的特性(Route和HttpXXX系列方法)来定义.本章,我们将 ...

  9. SQL*Plus中替换变量与定义变量

    替换变量 SQL*Plus中的替换变量又叫替代变量,它一般用来临时存储相关数据:在SQL语句之间传递值.一般使用&或&&前缀来指定替换变量. 关于使用替换变量,一般是利用其创建 ...

随机推荐

  1. c缺陷与陷阱笔记-第二章 语法陷阱

    1.函数的调用和番薯返回值是函数指针的声明 定义一个函数指针,例如  int (*fp)(float),这个函数的返回值是Int,参数是1个float类型,调用这个函数的方法是 (*fp)(),还有f ...

  2. [itint5]Excel数转换

    http://www.itint5.com/oj/#23 这里就是26进制的转换,但是要注意没有0,A就是1,Z就是26.所以要想象成从0开始,才能用原来的方法计算. //将十进制数转换为excel数 ...

  3. 【转】linux C++ 获取文件信息 stat函数详解

    stat函数讲解 表头文件:    #include <sys/stat.h>             #include <unistd.h>定义函数:    int stat ...

  4. Servlet课程0426(八)Servlet分页技术

    Welcome.java //登录界面 package com.tsinghua; import javax.servlet.http.*; import java.io.*; import java ...

  5. 211. Add and Search Word - Data structure design

    题目: Design a data structure that supports the following two operations: void addWord(word) bool sear ...

  6. git svn rebase出现了checksum mismatch的错误

    http://stackoverflow.com/questions/3156744/git-svn-rebase-checksum-mismatch This solution was the on ...

  7. word文档左侧显示目录

    word2007  选择word的视图,然后选择文档结构图

  8. 理解Java对象序列化(二)

    关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结.此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制.在撰写本文时,既参考了Th ...

  9. Is there a way for me to run Adb shell as root without typing in 'su'?

    Orginal artical :http://android.stackexchange.com/questions/5884/is-there-a-way-for-me-to-run-adb-sh ...

  10. CQOIX2007余数之和

    朴素能得个差不多吧…… 这题改进算法真恶心 pascal一直过不了,难道非得转c++? 代码:(pascal) var n,k,i,l,r,m:longint; ans:qword; function ...