通过栅格类型扩展使ArcGIS 支持更多传感器类型

1  WHAT:什么是栅格类型?

ArcGIS海量影像管理解决方案推出已经有一年时间了,相信很多朋友已经对ArcGIS中如何管理海量影像数据有了大致了解。ArcGIS 10.0中推出了适用于海量影像管理的镶嵌数据集模型(Mosaic Dataset),单个镶嵌数据集就可以管理数百万景,不同时相、不同分辨率、不同坐标系、不同空间位置的影像。

为了将各种来源,形式各异的影像数据导入镶嵌数据集中,我们需要为影像指定“栅格类型(Raster Type)”。简单说,栅格类型就是对各种影像数据的分门别类,而这种分类,基本上是按照卫星传感器类型、航摄相机类型进行划分的。下图是ArcGIS内置支持的一些栅格类型:

这里需要将栅格类型和栅格数据格式进行区分:栅格数据格式用于定义栅格像素如何存储,例如行列数、波段数、像素值等。而栅格类型则定义了如何识别影像关联的元数据,以及为影像定义的处理函数,如波段组合、全色融合等。

为了更好地理解栅格类型,我们可以打个比方:你要去电影院看电影,除了人要进去,还需要电影票,如果是3D电影,还需要带上3D眼镜。栅格影像数据入库,就相当于去看电影的过程:除了栅格影像数据本身要进去,还需要元数据信息(电影票),如果是多波段影像,可能还需要进行波段组合(看3D需要3D眼镜)。

2  WHEN:什么时候需要栅格类型扩展?

那么既然ArcGIS已经默认支持这么多的栅格类型,我们还有必要进行栅格类型扩展吗?当然需要了,在下面几个常见的场景中,栅格类型扩展是必须的:

1)       需要管理处理过的成果影像:很多单位会对影像数据进行各种处理,例如几何纠正、大气校正、影像拼接等等。经过这些处理的影像,就可能产生了自定义格式的元数据,这样就需要进行栅格类型扩展。

2)       需要管理国产卫星影像:很多单位都有国产卫星影像数据,例如风云系列、北京系列、环境星系列等等。这些卫星的元数据格式,也都形式各异,由于并不是国际通用的商业卫星,因此也需要进行栅格类型扩展。

3)       需要管理军用卫星影像:这类影像元数据格式可能就更加神秘了,如果读者有幸参与相关项目,则必须要通过栅格类型扩展才能管理这类影像。

4)       需要管理航飞拍摄的影像:航片也是影像数据的最大来源之一,比如无人机拍摄等。根据搭载的相机不同,生成的元数据也不尽相同,因此进行栅格类型扩展也是必要的。

那么有哪些影像是不需要进行栅格类型扩展就可以入库的呢?读者可以参考ArcGIS帮助中的介绍:

卫星传感器栅格类型:http://resources.arcgis.com/en/help/#/Satellite_sensor_raster_types/009t000001z6000000/

航空影像栅格类型:http://resources.arcgis.com/en/help/#/Aerial_imagery_raster_types/009t000001zp000000/

3  HOW:如何进行栅格类型扩展

OK,到这里,你已经了解了什么是栅格类型,以及在什么情况下需要进行栅格类型扩展,现在是时候学习如何进行栅格类型扩展了。

栅格类型扩展,实际上是通过ArcObjects开发来完成的。读者不必担心开发的难度,因为ArcGIS提供了非常完善的示例,而开发人员需要做的事情,其实就是给栅格类型扩展起个名字,然后去解析一下元数据文件,再根据需要定义一些预处理函数。

3.1 程序结构

在ArcObjects安装目录下,可以找到栅格类型扩展的示例程序,例如:C:\ArcGIS\DeveloperKit10.1\Samples\ArcObjectsNet\CustomRasterType\CSharp\ThumbnailBuilder,打开示例程序,可以看到ThumbnailBuilder.cs文件,其中包含了一个接口和两个类,如下图所示:

其中IThumbnailBuilder只包含一个属性,InnerRasterBuilder,表示栅格类型内置的构造器。

ThumbnailFactory则实现了IRasterTypeFactory接口,主要包含COM注册/反注册函数,栅格类型的名称,以及调用栅格类型构造器的流程。

ThumbnailBuilder则是具体的栅格类型构造器,其中包含了栅格类型扩展的具体处理算法。

3.2 实战案例

本例中,我们参考示例程序,但考虑到实际应用,加入了其他的功能,例如:记录操作日志,解析元数据文件等。程序结构如下图所示:

上述文件分别为:

1)       CgcImageryBuilder:栅格类型构造器,执行具体的栅格类型构造操作;

2)       CgcRasterTypeFactory:栅格类型工厂,定义栅格类型名称,提供COM注册函数,提供栅格类型构造器调用流程;

3)       BuilderHelper:一些通用的处理函数,比如获取镶嵌数据集默认字段、自定义字段,或者添加字段等;

4)       LogHelper:记录操作日志,写日志文件;

MetaReader:包含元数据解析函数,将指定格式的元数据文件,读取为标注的键值对。

3.3 主要代码

3.3.1 IAppendedBuilder

[csharp] view
plain
 copy

  1. interface IAppendedBuilder : IRasterBuilder
  2. {
  3. ///<summary>
  4. /// 附加的内置目标Raster Builder对象
  5. ///</summary>
  6. IRasterBuilder InnerRasterBuilder
  7. {
  8. get;
  9. set;
  10. }
  11. }

3.3.2 CgcRasterTypeFactory

[csharp] view
plain
 copy

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Runtime.InteropServices;
  6. using ESRI.ArcGIS.esriSystem;
  7. using ESRI.ArcGIS.DataSourcesRaster;
  8. using esricd.demos.cgc.imagery.util;
  9. namespace esricd.demos.cgc.imagery.ext
  10. {
  11. /// <summary>
  12. /// 重庆地理信息中心影像数据栅格类型工厂
  13. /// </summary>
  14. [Guid("AE299550-610F-44B6-9069-5C6E5CDC2F80")]
  15. [ClassInterface(ClassInterfaceType.None)]
  16. [ProgId("RasterTypeExtension.CgcRasterTypeFactory")]
  17. [ComVisible(true)]
  18. public class CgcRasterTypeFactory : IRasterTypeFactory
  19. {
  20. #region 私有成员
  21. public const string CGC_IMAGERY = "重庆地理信息中心影像类型";
  22. private IStringArray myRasterTypeNames;
  23. private UID myUID;
  24. public CgcRasterTypeFactory()
  25. {
  26. //指定栅格类型的名称,这些类型可以与扩展的栅格构造器关联
  27. myRasterTypeNames = new StrArrayClass();
  28. myRasterTypeNames.Add(CGC_IMAGERY);
  29. myUID = new UIDClass();
  30. myUID.Value = "{AE299550-610F-44B6-9069-5C6E5CDC2F80}";
  31. }
  32. #endregion
  33. #region IRasterTypeFactory 成员
  34. public ESRI.ArcGIS.esriSystem.UID CLSID
  35. {
  36. get { return myUID; }
  37. }
  38. public IRasterType CreateRasterType(string RasterTypeName)
  39. {
  40. try
  41. {
  42. // 1-首先获取内置栅格类型(Raster Dataset)
  43. IRasterTypeName theRasterTypeName = new RasterTypeNameClass();
  44. theRasterTypeName.Name = "Raster Dataset";//接受指向.art文件的路径,或内置栅格类型的名称
  45. IRasterType theRasterType = (IRasterType)(((IName)theRasterTypeName).Open());
  46. if (theRasterType == null)
  47. {
  48. LogHelper.AddMessage("错误:未找到栅格类型 " + theRasterTypeName.Name);
  49. return null;
  50. }
  51. // 2-在内置栅格类型(Raster Dataset)的栅格构造器上附加自定义的栅格构造器
  52. IAppendedBuilder pAppendedBuilder = new CgcImageryBuilder();
  53. pAppendedBuilder.InnerRasterBuilder = theRasterType.RasterBuilder;
  54. theRasterType.RasterBuilder = pAppendedBuilder as IRasterBuilder;
  55. // 3-将内置栅格类型(Raster Dataset)的名称改为自定义栅格类型的名称
  56. IName theName = theRasterType.FullName;
  57. ((IRasterTypeName)theName).Name = RasterTypeName;
  58. return theRasterType;
  59. }
  60. catch (Exception ex)
  61. {
  62. LogHelper.AddMessage("创建栅格类型 " + RasterTypeName + " 失败:" + ex.Message);
  63. return null;
  64. }
  65. }
  66. public string Name
  67. {
  68. get { return "Cgc Raster Type Factory"; }
  69. }
  70. public ESRI.ArcGIS.esriSystem.IStringArray RasterTypeNames
  71. {
  72. get { return myRasterTypeNames; }
  73. }
  74. #endregion
  75. #region COM 注册函数
  76. [ComRegisterFunction()]
  77. static void Reg(string regKey)
  78. {
  79. ESRI.ArcGIS.ADF.CATIDs.RasterTypeFactory.Register(regKey);
  80. }
  81. [ComUnregisterFunction()]
  82. static void Unreg(string regKey)
  83. {
  84. ESRI.ArcGIS.ADF.CATIDs.RasterTypeFactory.Unregister(regKey);
  85. }
  86. #endregion
  87. }
  88. }

3.3.3 CgcImageryBuilder

[csharp] view
plain
 copy

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Runtime.InteropServices;
  6. using ESRI.ArcGIS.DataSourcesRaster;
  7. using ESRI.ArcGIS.esriSystem;
  8. using ESRI.ArcGIS.Geodatabase;
  9. using esricd.demos.cgc.imagery.util;
  10. using ESRI.ArcGIS.Geometry;
  11. namespace esricd.demos.cgc.imagery.ext
  12. {
  13. [Guid("A1AE3CF4-2C9E-4A9C-9287-C8D9BF182F64")]
  14. [ClassInterface(ClassInterfaceType.None)]
  15. [ProgId("RasterTypeExtension.CgcImageryBuilder")]
  16. [ComVisible(true)]
  17. public class CgcImageryBuilder : IAppendedBuilder, IPersistVariant, IRasterBuilderInit
  18. {
  19. #region 私有成员
  20. public IRasterBuilder innerRasterBuilder; // Inner Raster Builder
  21. private UID myUID; // UID for the Builder.
  22. #endregion
  23. #region 附加构造器成员
  24. /// <summary>
  25. /// 构造函数不能含参数,否则报内存访问错误
  26. /// </summary>
  27. public CgcImageryBuilder()
  28. {
  29. innerRasterBuilder = null;
  30. myUID = new UIDClass();
  31. myUID.Value = "{A1AE3CF4-2C9E-4A9C-9287-C8D9BF182F64}";
  32. }
  33. public IRasterBuilder InnerRasterBuilder
  34. {
  35. get
  36. {
  37. return innerRasterBuilder;
  38. }
  39. set
  40. {
  41. innerRasterBuilder = value;
  42. }
  43. }
  44. #endregion
  45. #region IRasterBuilder 成员
  46. /// <summary>
  47. /// AuxiliaryFieldAlias property gets and sets the Auxiliary(辅助、备用) fields Alias
  48. /// present in the raster builder
  49. /// </summary>
  50. public ESRI.ArcGIS.esriSystem.IPropertySet AuxiliaryFieldAlias
  51. {
  52. get
  53. {
  54. return innerRasterBuilder.AuxiliaryFieldAlias;
  55. }
  56. set
  57. {
  58. innerRasterBuilder.AuxiliaryFieldAlias = value;
  59. }
  60. }
  61. /// <summary>
  62. /// Flag to specify whether the Raster Builder can build items in place.
  63. /// </summary>
  64. public bool CanBuildInPlace
  65. {
  66. get { return innerRasterBuilder.CanBuildInPlace; }
  67. }
  68. /// <summary>
  69. /// Prepare the Raster Type for generating Crawler items
  70. /// </summary>
  71. /// <param name="pCrawler">Crawler to use to generate the crawler items</param>
  72. public void BeginConstruction(IDataSourceCrawler pCrawler)
  73. {
  74. innerRasterBuilder.BeginConstruction(pCrawler);
  75. }
  76. /// <summary>
  77. /// Construct a Unique Resource Identifier (URI)
  78. /// for each crawler item
  79. /// </summary>
  80. /// <param name="crawlerItem">Crawled Item from which the URI is generated</param>
  81. public void ConstructURIs(object crawlerItem)
  82. {
  83. innerRasterBuilder.ConstructURIs(crawlerItem);
  84. }
  85. /// <summary>
  86. /// Finish Construction of the URI‘s
  87. /// </summary>
  88. /// <returns></returns>
  89. public IItemURIArray EndConstruction()
  90. {
  91. return innerRasterBuilder.EndConstruction();
  92. }
  93. /// <summary>
  94. /// Generate the next URI.
  95. /// </summary>
  96. /// <returns>The URI generated.</returns>
  97. public IItemURI GetNextURI()
  98. {
  99. return innerRasterBuilder.GetNextURI();
  100. }
  101. /// <summary>
  102. /// Get the recommended data crawler for the Raster Type based on
  103. /// properties provided by the user.
  104. /// </summary>
  105. /// <param name="pDataSourceProperties">List of properties provided by the user</param>
  106. /// <returns></returns>
  107. public IDataSourceCrawler GetRecommendedCrawler(IPropertySet pDataSourceProperties)
  108. {
  109. return innerRasterBuilder.GetRecommendedCrawler(pDataSourceProperties);
  110. }
  111. /// <summary>
  112. /// Check if the item provided is "stale不新鲜的,过时的" or not valid
  113. /// </summary>
  114. /// <param name="pItemURI">URI for the item to be checked</param>
  115. /// <returns></returns>
  116. public bool IsStale(IItemURI pItemURI)
  117. {
  118. return innerRasterBuilder.IsStale(pItemURI);
  119. }
  120. /// <summary>
  121. /// Properties associated with the Raster Type
  122. /// </summary>
  123. public IPropertySet Properties
  124. {
  125. get
  126. {
  127. return innerRasterBuilder.Properties;
  128. }
  129. set
  130. {
  131. innerRasterBuilder.Properties = value;
  132. }
  133. }
  134. /// <summary>
  135. /// 添加辅助字段,所有非镶嵌数据集默认字段均可视为辅助字段
  136. /// </summary>
  137. public IFields AuxiliaryFields
  138. {
  139. get
  140. {
  141. ESRI.ArcGIS.DataSourcesRaster.IRasterBuilder pRasterBuilder = innerRasterBuilder;
  142. IFields myFields = pRasterBuilder.AuxiliaryFields;
  143. try
  144. {
  145. IMosaicDataset pMosaicDataset = ((IRasterBuilderInit)innerRasterBuilder).MosaicDataset;
  146. IFields pAllFields = BuilderHelper.GetMosaicDatasetFields(pMosaicDataset);
  147. Dictionary<string, esriFieldType> lstCustomFields = BuilderHelper.GetMosaicDatasetCustomFields(pAllFields);
  148. foreach (KeyValuePair<string,esriFieldType> fieldItem in lstCustomFields)
  149. {
  150. int count = myFields.FindField(fieldItem.Key);
  151. if (count == -1)
  152. {
  153. BuilderHelper.AddNewField(myFields, fieldItem.Key, fieldItem.Value);
  154. pRasterBuilder.AuxiliaryFields = myFields;
  155. }
  156. }
  157. }
  158. catch (Exception ex)
  159. {
  160. LogHelper.AddMessage("添加辅助字段失败:" + ex.ToString());
  161. }
  162. return innerRasterBuilder.AuxiliaryFields;
  163. }
  164. set
  165. {
  166. innerRasterBuilder.AuxiliaryFields = value;
  167. }
  168. }
  169. /// <summary>
  170. /// 构建栅格数据项,可在此对每一项执行附加的构建操作.
  171. /// </summary>
  172. /// <param name="pItemURI"></param>
  173. /// <returns></returns>
  174. public IBuilderItem Build(IItemURI pItemURI)
  175. {
  176. try
  177. {
  178. // 执行内置的构建操作(参数为待构建项的URI)
  179. LogHelper.AddMessage("正在读取栅格数据:" + pItemURI.Key);
  180. IBuilderItem pBuilderItem = innerRasterBuilder.Build(pItemURI);
  181. //获取镶嵌数据集字段列表,以便根据字段别名提取元数据项
  182. IMosaicDataset md = ((IRasterBuilderInit)innerRasterBuilder).MosaicDataset;
  183. IFields pFields = BuilderHelper.GetMosaicDatasetFields(md);
  184. //获取栅格数据的属性,以便将元数据项写入属性表中
  185. IPropertySet pPropSet = pBuilderItem.Dataset.Properties;
  186. //读写元数据
  187. MetaReader.ReadCgcImageryMeta(pItemURI.Key, pFields, pPropSet);
  188. LogHelper.AddMessage("已完成栅格数据添加。");
  189. LogHelper.WriteToLogFile();
  190. return pBuilderItem;
  191. }
  192. catch (Exception ex)
  193. {
  194. LogHelper.AddMessage("构建栅格数据失败:" + ex.ToString());
  195. LogHelper.WriteToLogFile();
  196. return null;
  197. }
  198. }
  199. #endregion
  200. #region IPersistVariant 成员
  201. /// <summary>
  202. /// UID for the object implementing the Persist Variant
  203. /// </summary>
  204. UID IPersistVariant.ID
  205. {
  206. get { return myUID; }
  207. }
  208. /// <summary>
  209. /// Load the object from the stream provided
  210. /// </summary>
  211. /// <param name="Stream">Stream that represents the serialized Raster Type</param>
  212. void IPersistVariant.Load(IVariantStream Stream)
  213. {
  214. string name = (string)Stream.Read();
  215. innerRasterBuilder = (IRasterBuilder)Stream.Read(); // Load the innerRasterBuilder from the stream.
  216. }
  217. /// <summary>
  218. /// Same the Raster Type to the stream provided
  219. /// </summary>
  220. /// <param name="Stream">Stream to serialize the Raster Type into</param>
  221. void IPersistVariant.Save(IVariantStream Stream)
  222. {
  223. Stream.Write("CgcImageryBuilder");
  224. Stream.Write(innerRasterBuilder); // Save the innerRasterBuilder into the stream.
  225. }
  226. #endregion
  227. #region IRasterBuilderInit 成员
  228. /// <summary>
  229. /// Default spatial reference for the MD.
  230. /// </summary>
  231. ISpatialReference IRasterBuilderInit.DefaultSpatialReference
  232. {
  233. get
  234. {
  235. return ((IRasterBuilderInit)innerRasterBuilder).DefaultSpatialReference;
  236. }
  237. set
  238. {
  239. ((IRasterBuilderInit)innerRasterBuilder).DefaultSpatialReference = value;
  240. }
  241. }
  242. /// <summary>
  243. /// Parent mosaic dataset for the Raster Builder.
  244. /// </summary>
  245. IMosaicDataset IRasterBuilderInit.MosaicDataset
  246. {
  247. get
  248. {
  249. return ((IRasterBuilderInit)innerRasterBuilder).MosaicDataset;
  250. }
  251. set
  252. {
  253. ((IRasterBuilderInit)innerRasterBuilder).MosaicDataset = value;
  254. }
  255. }
  256. /// <summary>
  257. /// The raster type operation helper object associated with this raster type.
  258. /// </summary>
  259. IRasterTypeOperation IRasterBuilderInit.RasterTypeOperation
  260. {
  261. get
  262. {
  263. return ((IRasterBuilderInit)innerRasterBuilder).RasterTypeOperation;
  264. }
  265. set
  266. {
  267. ((IRasterBuilderInit)innerRasterBuilder).RasterTypeOperation = value;
  268. }
  269. }
  270. /// <summary>
  271. /// Tracker for when cancel is pressed.
  272. /// </summary>
  273. ITrackCancel IRasterBuilderInit.TrackCancel
  274. {
  275. get
  276. {
  277. return ((IRasterBuilderInit)innerRasterBuilder).TrackCancel;
  278. }
  279. set
  280. {
  281. ((IRasterBuilderInit)innerRasterBuilder).TrackCancel = value;
  282. }
  283. }
  284. #endregion
  285. }
  286. }

扩展开发完成、注册之后,可以在栅格类型中看到自定义的栅格类型,如下图最后一个:

这样在影像数据导入镶嵌数据集的时候,就可以选择扩展的栅格类型,保证元数据信息正确入库管理,为下一步影像管理、应用打下坚实的基础。

本文仅介绍栅格类型扩展的技术路线和关键代码,日志记录和其他辅助函数则没有上传,如需完整代码,请在评论中留下邮箱地址。如有疑问,也请在评论中留言探讨。

转自http://blog.csdn.net/esrichinacd/article/details/7844494

时间: 2024-10-13 05:57:56

通过栅格类型扩展使ArcGIS 支持更多传感器类型的相关文章

【nginx笔记】系统参数设置-使Nginx支持更多并发请求的TCP网络参数

首先,需要修改/etc/sysctl.conf来更改内核参数.例如,最常用的配置: fs.file-max = 999999 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_max_tw_buckets = 5000 net.ipv4.ip_local_port_range = 1024 61000 net.ipv4.tcp_rmem =

扩展 StackExchange.Redis 支持实体

一.StackExchange.Redis StackExchange.Redis是由Stack Overflow开发的C#语言Redis客户端,使用广泛,本文针对 StackExchange.Redis 进一步扩展使之支持实体 二. 使用Demo 1. 安装 Install-Package Apteryx.StackexChange.Redis.Extend 2. Demo using Apteryx.StackExChange.Redis.Extend.Service; 移步我的项目http

如何配置IIS使其支持APK文件的下载

如何配置IIS使其支持APK文件的下载APK文件是安卓的安装程序的文件,IIS里的MIME里默认是不支持的.如果没有配置MIME时,直接输入网址要下载APK文件时,会提示找不到此文件.这里教你如何配置IIS的MIME设置,使其可以支持APK文件的下载.1.在管理工具里打开Internet 信息服务(IIS)管理器.然后选择需要配置的网站.2.右侧的界面中会显示该网站的所有功能配置,我们选择并点击进入“MIME类型”3.在右侧的操作区选择点击“添加”MIME.4.在弹出的添加窗口里的文件扩展名输入

PHPnow开启PHP扩展里openssl支持的方法

PHPnow 是 Win32 下绿色的 Apache + PHP + MySQL 环境套件包.简易安装.快速搭建支持虚拟主机的 PHP 环境.更多介绍<PHP服务套件 PHPnow1.5.6>及安装<PHPnow 快速搭建Apache+PHP+MySQL环境 >.下面将分享PHPnow开启PHP扩展里openssl支持的方法. 打开你集成包的文件夹,找到“/php-5.2.x-Win32/php-apache2handler.ini”这文件,随便用记事本打开,查找 ;extensi

9.1.2 使用类型扩展追加成员

在上一节我们提到过,可以为任何 F# 数据类型添加成员;现在,我们将使用差别联合来演示.这种种方法能够添加成员,而不需要修改任何原始代码.这样,我们将能够保留原始类型和原始的函数声明,不作修改,然后添加成员. 我们将扩展第五章声明 schedule 类型的示例,这个类型表示的事件可以只发生一次,或重复发生,或从不发生.除了数据类型之外,我们还创建了计算事件下一次发生时间的函数.清单 9.4 是代码稍作修改后的版本,我们使代码更紧凑,并使用简单的工具函数,重构了模式匹配中的 Once 分支:如果想

HTML5表单、一些新增的输入类型以及为不支持新特性的浏览器提供解决方案

我们先来看一下这么样一个表单: 一.一步一步来分析下代码: 1 <form id="redemption" method="post"> 2 <hgroup> 3 <h1>Oscar Redemption</h1> 4 <h2>Here's your chance to set the record straight: tell us what 5 year the wrong film got nomin

PIE使IE支持CSS3圆角盒阴影与渐变渲染

PIE使IE支持CSS3圆角盒阴影与渐变渲染 http://css3pie.com/download/

配置 squid 使其支持 访问https站点

需求:让用户通过squid访问https网站 注意和配置squid使其支持https不同 网上的资料基本都是给squid配置一个证书,但直觉告诉我这并不能解决我们的问题 进入正题,通过之前配置好的squid访问http站点可以正常访问, 但无法访问https开头的网站 查找问题最好的方法就是分析日志 access.log中发现如下信息 NONE/400 4280CONNECT error:method-not-allowed - NONE/- text/html 查看 squid.conf ,默

Util应用程序框架公共操作类(十):可空值类型扩展

当你使用可空的值类型时,你会发现取值很不方便,比如Guid? obj,你要从obj中获取值,可以使用Value属性obj. Value,但obj可能为null,这时候就会抛出一个异常. 可空值类型提供了一个HasValue属性,它可以识别出obj是不是一个null值,每当你获取可空值都需要加上这个判断if(value.HasValue){ var value = obj.Value;}. 下面我们通过几个扩展方法,把判断封装起来. 在Util项目中添加Extensions.Nullable.cs