关于C#中逗号分隔字符串转泛型数组的说明

在处理Web表单请求时,相同name值的数据会以逗号分隔的字符串形式提交到服务端,以下示例如何在C#中将此类字符串转换为实际需要的数据类型:

// 定义逗号分隔字符串
string value = "a,b,c,d";
// 获取泛型类型
Type type = typeof(T);
// 判断泛型是否为数组
if (type.IsArray)
{
    // 将字符串以逗号拆分
    string[] values = value.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
    // 创建泛型对应的数组实例
    Array array = (Array)Activator.CreateInstance(type, values.Length);
    // 获取泛型数组元素类型
    Type elementType = type.GetElementType();
    // 将字符串逐个转换为泛型数组元素类型,并添加到数组对应索引处
    for (int i = 0, length = values.Length; i < length; i++)
    {
        array.SetValue(Convert.ChangeType(values[i], elementType), i);
    }
    // 获取泛型数组
    return (T)(object)array;
}
else
{
    // 不是数组则直接尝试将字符串转换为泛型类型
    return (T)Convert.ChangeType(value, type);
}

关于Castle.Windsor中实现属性类注解自动注入的说明

Castle是.NET平台上的一个开源项目,为企业级开发和WEB应用程序开发提供完整的服务。Windsor是Castle下的一个项目,用于提供IOC的解决方案。IOC被称为控制反转或者依赖注入(Inversion of Control)
以下介绍如何通过Castle.Windsor实现对类的成员属性或字段进行自动注入:

一、自动注入核心代码:

using Castle.Core;
using Castle.Core.Resource;
using Castle.MicroKernel;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
using System;
using System.Collections.Generic;
using System.Reflection;

// 成员注解自动注入属性类,可注解字段和属性
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class InstanceAttribute : Attribute
{
    // 获取或设置需要注入对象的ID值
    public string ID
    {
        get;
        set;
    }

    // 获取或设置需要注入对象的类型
    public Type Type
    {
        get;
        set;
    }
}

// 成员注解自动注入功能类,继承Castle.Windsor的WindsorContainer
public class InstanceProvider : WindsorContainer
{
    // 定义Castle.Windsor在程序配置文件中的节点
    private static readonly string ConfigRoot = "windsor";
    // 定义类的成员注解信息字典
    private static Dictionary<Type, Dictionary<MemberInfo, InstanceAttribute>> _InstanceMembers = new Dictionary<Type, Dictionary<MemberInfo, InstanceAttribute>>();

    // 构造函数初始化程序默认配置文件的Castle.Windsor配置信息(也可以指定其他配置文件)
    public InstanceProvider()
      : base(new XmlInterpreter(new ConfigResource(InstanceProvider.ConfigRoot)))
    {
        // 绑定IoC组件创建事件
        this.Kernel.ComponentCreated += this.Kernel_ComponentCreated;
    }

    // 获取类型的成员注解信息
    private Dictionary<MemberInfo, InstanceAttribute> GetInstanceMembers(Type type)
    {
        // 定义类型的成员注解信息
        Dictionary<MemberInfo, InstanceAttribute> instanceMembers;
        // 获取Castle.Windsor的IoC接口
        IKernel kernel = this.Kernel;
        // 线程安全锁,防止多线程对成员注解信息字典操作的冲突
        lock (InstanceProvider._InstanceMembers)
        {
            // 判断当前类型是否已存在成员注解信息
            if (InstanceProvider._InstanceMembers.ContainsKey(type))
            {
                // 根据类型获取所属的成员注解信息
                instanceMembers = InstanceProvider._InstanceMembers[type];
            }
            else
            {
                // 新建成员注解信息
                instanceMembers = new Dictionary<MemberInfo, InstanceAttribute>();
                // 获取成员注解的属性类类型
                Type instanceAttributeType = typeof(InstanceAttribute);
                // 获取类型的成员信息
                MemberInfo[] memberInfos = type.GetMembers();
                foreach (MemberInfo memberInfo in memberInfos)
                {
                    switch (memberInfo.MemberType)
                    {
                        case MemberTypes.Field:
                            // 获取字段成员
                            FieldInfo fieldInfo = memberInfo as FieldInfo;
                            // 获取成员注解对象
                            object[] fieldInstanceAttributes = memberInfo.GetCustomAttributes(instanceAttributeType, false);
                            // 查找有效的字段注解信息
                            foreach (InstanceAttribute instanceAttribute in fieldInstanceAttributes)
                            {
                                // 判断是否设置了需要注入对象的ID值
                                if (instanceAttribute.ID == null)
                                {
                                    // 定义IoC组件类型
                                    Type componentType;
                                    // 判断是否设置了需要注入对象的类型
                                    if (instanceAttribute.Type == null)
                                    {
                                        // 没有设置需要注入对象的类型则使用字段类型
                                        componentType = fieldInfo.FieldType;
                                    }
                                    else
                                    {
                                        // 设置IoC组件类型
                                        componentType = instanceAttribute.Type;
                                    }
                                    // 没有设置需要注入对象的ID值则根据组件类型判断是否存在对应的IoC组件
                                    if (kernel.HasComponent(componentType))
                                    {
                                        // 添加成员注解信息项
                                        instanceMembers.Add(memberInfo, instanceAttribute);
                                        break;
                                    }
                                }
                                else
                                {
                                    // 根据注入对象的ID值判断是否存在Ioc组件
                                    if (kernel.HasComponent(instanceAttribute.ID))
                                    {
                                        // 添加成员注解信息项
                                        instanceMembers.Add(memberInfo, instanceAttribute);
                                        break;
                                    }
                                }
                            }
                            break;
                        case MemberTypes.Property:
                            // 获取属性成员
                            PropertyInfo propertyInfo = memberInfo as PropertyInfo;
                            // 判断属性成员是否可读写,并且Get、Set访问器的静态性是否一致
                            MethodInfo getMethodInfo = propertyInfo.GetGetMethodInfo();
                            MethodInfo setMethodInfo = propertyInfo.GetSetMethodInfo();
                            if (getMethodInfo != null && setMethodInfo != null && getMethodInfo.IsStatic == setMethodInfo.IsStatic)
                            {
                                // 获取属性注解对象
                                object[] propertyInstanceAttributes = memberInfo.GetCustomAttributes(instanceAttributeType, false);
                                // 查找有效的属性注解信息
                                foreach (InstanceAttribute instanceAttribute in propertyInstanceAttributes)
                                {
                                    // 判断是否设置了需要注入对象的ID值
                                    if (instanceAttribute.ID == null)
                                    {
                                        // 定义IoC组件类型
                                        Type componentType;
                                        // 判断是否设置了需要注入对象的类型
                                        if (instanceAttribute.Type == null)
                                        {
                                            // 没有设置需要注入对象的类型则使用属性类型
                                            componentType = propertyInfo.PropertyType;
                                        }
                                        else
                                        {
                                            // 设置IoC组件类型
                                            componentType = instanceAttribute.Type;
                                        }
                                        // 没有设置需要注入对象的ID值则根据组件类型判断是否存在对应的IoC组件
                                        if (kernel.HasComponent(componentType))
                                        {
                                            // 添加属性注解信息项
                                            instanceMembers.Add(memberInfo, instanceAttribute);
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        // 根据注入对象的ID值判断是否存在Ioc组件
                                        if (kernel.HasComponent(instanceAttribute.ID))
                                        {
                                            // 添加属性注解信息项
                                            instanceMembers.Add(memberInfo, instanceAttribute);
                                            break;
                                        }
                                    }
                                }
                            }
                            break;
                        default:
                            break;
                    }
                }
                // 将获取到的成员注解信息保存到成员注解信息字典以备下次获取
                InstanceProvider._InstanceMembers.Add(type, instanceMembers);
            }
        }
        // 返回成员注解信息
        return instanceMembers;
    }

    // 为对象进行属性注入,静态类可通过传入Type对象进行注入
    public void Instance(object obj)
    {
        // 获取需要注入对象的类型
        Type type;
        // 判断对象是否是Type对象
        if (obj is Type)
        {
            // 直接强制转换为Type对象
            type = obj as Type;
        }
        else
        {
            // 获取对象的类型
            type = obj.GetType();
        }
        // 获取对象的成员注入信息
        Dictionary<MemberInfo, InstanceAttribute> instanceMembers = this.GetInstanceMembers(type);
        // 获取Castle.Windsor的IoC接口
        IKernel kernel = this.Kernel;
        // 开始进行成员注入
        foreach (KeyValuePair<MemberInfo, InstanceAttribute> item in instanceMembers)
        {
            switch (item.Key.MemberType)
            {
                case MemberTypes.Field:
                    // 获取字段注入信息
                    FieldInfo fieldInfo = item.Key as FieldInfo;
                    // 定义字段反射对象
                    object field;
                    // 根据字段是否是静态字段设置反射对象
                    if (fieldInfo.IsStatic)
                    {
                        field = null;
                    }
                    else
                    {
                        field = obj;
                    }
                    // 判断字段是否已经被赋值,只对未赋值字段进行注入
                    if (fieldInfo.GetValue(field) == null)
                    {
                        // 定义IoC组件类型
                        Type componentType;
                        // 判断是否设置了需要注入对象的类型
                        if (item.Value.Type == null)
                        {
                            // 没有设置需要注入对象的类型则使用字段类型
                            componentType = fieldInfo.FieldType;
                        }
                        else
                        {
                            // 设置IoC组件类型
                            componentType = item.Value.Type;
                        }
                        // 定义需要注入的字段值
                        object value;
                        // 判断是否设置了需要注入对象的ID值
                        if (item.Value.ID == null)
                        {
                            // 没有设置需要注入对象的ID值则根据类型获取对应的IoC组件
                            value = kernel.Resolve(componentType);
                        }
                        else
                        {
                            // 根据注入对象的ID值获取Ioc组件
                            value = kernel.Resolve(item.Value.ID, componentType);
                        }
                        // 通过反射为字段进行赋值
                        fieldInfo.SetValue(field, value);
                    }
                    break;
                case MemberTypes.Property:
                    // 获取属性注入信息
                    PropertyInfo propertyInfo = item.Key as PropertyInfo;
                    // 定义属性反射对象
                    object property;
                    // 根据属性是否是静态属性设置反射对象
                    MethodInfo getMethodInfo = propertyInfo.GetGetMethod();
                    MethodInfo setMethodInfo = propertyInfo.GetSetMethod();
                    if (getMethodInfo.IsStatic && setMethodInfo.IsStatic)
                    {
                        property = null;
                    }
                    else
                    {
                        property = obj;
                    }
                    // 判断属性是否已经被赋值,只对未赋值属性进行注入
                    if (propertyInfo.GetValue(property, null) == null)
                    {
                        // 定义IoC组件类型
                        Type componentType;
                        // 判断是否设置了需要注入对象的类型
                        if (item.Value.Type == null)
                        {
                            // 没有设置需要注入对象的类型则使用属性类型
                            componentType = propertyInfo.PropertyType;
                        }
                        else
                        {
                            // 设置IoC组件类型
                            componentType = item.Value.Type;
                        }
                        // 定义需要注入的属性值
                        object value;
                        // 判断是否设置了需要注入对象的ID值
                        if (item.Value.ID == null)
                        {
                            // 没有设置需要注入对象的ID值则根据类型获取对应的IoC组件
                            value = kernel.Resolve(componentType);
                        }
                        else
                        {
                            // 根据注入对象的ID值获取Ioc组件
                            value = kernel.Resolve(item.Value.ID, componentType);
                        }
                        // 通过反射为属性进行赋值
                        propertyInfo.SetValue(property, value, null);
                    }
                    break;
                default:
                    break;
            }
        }
    }

    // 订阅IoC组件创建事件
    private void Kernel_ComponentCreated(ComponentModel model, object instance)
    {
        // 在每次创建IoC组件时进行成员注入
        this.Instance(instance);
    }
}

二、调用说明:
定义类,并对需要进行注入的属性或字段标记InstanceAttribute注解属性类。创建InstanceProvider对象,通过调用Instance方法将传入的类实例对象进行属性或字段的注入。如果是静态类可通过传入其Type对象进行注入。

更多信息请参阅:Windsor | Castle Project

关于栅格数据中换算行列号和坐标系的说明

在进行栅格数据行列号和坐标系换算时需要注意以下两点:
1、栅格数据的行列号索引值一般以0开始;
2、坐标系方向与栅格行列索引方向是否相同。

一、将XY表示的坐标系值转换成对应栅格的行列号索引值:

// 栅格数据总行数
int rowCount = 100;
// 栅格数据总列数
int columnCount = 100;
// 栅格数据最小X坐标
double minX = 0;
// 栅格数据最小Y坐标
double minY = 0;
// 栅格数据最大X坐标
double maxX = 1000;
// 栅格数据最大Y坐标
double maxY = 1000;
// 需要换算的X坐标
double x = 400;
// 需要换算的Y坐标
double y = 400;
// 计算X轴方向每个栅格的坐标跨度(与栅格列相对应)
double dx = (maxX - minX) / (double)columnCount;
// 计算指定x坐标对应的栅格列索引值(坐标系X轴方向与栅格列索引方向相同)
int columnIndex = (int)((x - minX) / dx);
// 计算Y轴方向每个栅格的坐标跨度(与栅格行相对应)
double dy = (maxY - minY) / (double)rowCount;
// 计算指定y坐标对应的栅格行索引值(坐标系Y轴方向与栅格行索引方向相反)
int rowIndex = rowCount - (int)((y - minY) / dy) - 1;

二、将栅格的行列号索引值转换成以XY表示的坐标系值:

// 栅格数据总行数
int rowCount = 100;
// 栅格数据总列数
int columnCount = 100;
// 栅格数据最小X坐标
double minX = 0;
// 栅格数据最小Y坐标
double minY = 0;
// 栅格数据最大X坐标
double maxX = 1000;
// 栅格数据最大Y坐标
double maxY = 1000;
// 需要换算的栅格行索引值
int rowIndex = 40;
// 需要换算的栅格列索引值
int columnIndex = 40;
// 计算X轴方向每个栅格的坐标跨度(与栅格列相对应)
double dx = (maxX - minX) / (double)columnCount;
// 计算指定栅格列索引值对应的x坐标(坐标系X轴方向与栅格列索引方向相同,并取栅格的中心点作为栅格x坐标)
double x = (columnIndex + 0.5) * dx + minX;
// 计算Y轴方向每个栅格的坐标跨度(与栅格行相对应)
double dy = (maxY - minY) / (double)rowCount;
// 计算指定栅格行索引值对应的y坐标(坐标系Y轴方向与栅格行索引方向相反,并取栅格的中心点作为栅格y坐标)
double y = (rowCount - rowIndex - 0.5) * dy + minY;

关于C#中读取HDF4文件数据的说明

HDF4也被称为HDF(Hierarchical Data Format),可以存储不同类型的图像和数码数据的文件格式,并且可以在不同类型的机器上传输,同时还有统一处理这种文件格式的函数库。随着技术的发展,现存在两个完全不同的HDF文件版本:HDF4和HDF5,其中HDF4是第一个HDF版本。我们可以通过访问HDF4的官方网站(http://www.hdfgroup.org/products/hdf4/)查看其详细介绍。
csharp-read-hdf4-1
如果只是需要查看HDF4文件数据的话,我们可以从官网上下载并安装HDFView(一个简单的HDF文件查看工具):
csharp-read-hdf4-2
但是如果需要通过编程手段读写HDF4文件数据的话,我们就必须先从官网上下载并安装HDF4的函数库,然后再编程调用该函数库来实现对其的读写。
以下介绍如何通过C#方式读取HDF4文件的属性和数据。

一、安装HDF4函数库:
在HDF4官网下载页(http://www.hdfgroup.org/release4/obtain.html)下载并安装HDF4函数库。官网提供了多个系统平台的编译版本,在这里我们选择“Windows (32-bit)”平台:
csharp-read-hdf4-3
如在安装过程中发现环境变量无法写入,我们可以忽略设置环境变量选项。

二、引用HDF4函数库:
从官网中我们可以发现,现在的HDF4函数库只有提供C++和Fortran访问的版本,要想在C#程序中使用的话,我们需要通过DllImport方式引入。HDF4的函数库主要包含在hdf.dll和mfhdf.dll这两个库中,而常用来读取HDF4中数据的SD接口则在mdhdf.dll库中。

// 定义HDF4 SD接口函数库的文件位置
public const string MFHDF4_DLL = @"C:\Program Files (x86)\HDF_Group\HDF\4.2.11\bin\mfhdf.dll";

// 引入SDstart方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDstart", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDstart(string filename, int access_mode);

// 引入SDfindattr方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDfindattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDfindattr(int obj_id, string attr_name);

// 引入SDreadattr方法(字符串类型属性)
[DllImport(MFHDF4_DLL, EntryPoint = "SDreadattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDreadattr(int obj_id, int attr_index, StringBuilder attr_buffer);

// 引入SDreadattr方法(单精度浮点类型属性)
[DllImport(MFHDF4_DLL, EntryPoint = "SDreadattr", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDreadattr(int obj_id, int attr_index, float[] attr_buffer);

// 引入SDnametoindex方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDnametoindex", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDnametoindex(int sd_id, string sds_name);

// 引入SDselect方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDselect", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDselect(int sd_id, int sds_index);

// 引入SDgetinfo方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDgetinfo", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDgetinfo(int sds_id, StringBuilder sds_name, int[] rank, int[] dimsizes, int[] ntype, int[] num_attrs);

// 引入SDreaddata方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDreaddata", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDreaddata(int sds_id, int[] start, int[] stride, int[] edge, short[,] buffer);

// 引入SDendaccess方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDendaccess", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDendaccess(int sds_id);

// 引入SDend方法
[DllImport(MFHDF4_DLL, EntryPoint = "SDend", CallingConvention = CallingConvention.Cdecl)]
public static extern int SDend(int sd_id);

以上我们通过DllImport的方式引入读取HDF4文件所需要用到的相关SD接口函数,其中EntryPoint属性为对应的函数名称,CallingConvention属性必须设置为CallingConvention.Cdecl。由于个别函数返回的数据是通过指针引用在函数参数中的,因此我们需要通过StringBuilder来接收字符串数据,通过值类型数组(如float[]、int[]、short[]等)来接收数值型数据和数组型数据(不能通过ref或out参数返回)。

三、读取HDF4属性和数据:

// 要读取的HDF4文件
string hdf4File = "C:/test.hdf";
// 字符串类型属性
string sensor;
// 单精度浮点类型属性
float slope;
// 数据行数
int row;
// 数据列数
int column;
// 数据集数据
short[,] data;
// 函数调用状态
int status;
// 只读方式打开HDF4文件
int sd_id = SDstart(hdf4File, 1);
if (sd_id != -1)
{
    // 查找属性Sensor的索引号
    int attr_index = SDfindattr(sd_id, "Sensor");
    if (attr_index != -1)
    {
        // 读取Sensor属性,通过StringBuilder接收字符串数据
        StringBuilder buffer = new StringBuilder();
        status = SDreadattr(sd_id, attr_index, buffer);
        if (status != -1)
        {
            sensor = buffer.ToString();
        }
    }
    // 查找属性Slope的索引号
    attr_index = SDfindattr(sd_id, "Slope");
    if (attr_index != -1)
    {
        // 读取Slope属性,通过float[]接收单精度浮点数据
        float[] buffer = new float[1];
        status = SDreadattr(sd_id, attr_index, buffer);
        if (status != -1)
        {
            slope = buffer[0];
        }
    }
    // 查找数据集DATA的索引号
    int sds_indes = SDnametoindex(sd_id, "DATA");
    // 选中数据集
    int sds_id = SDselect(sd_id, sds_indes);
    if (sds_id != -1)
    {
        // 数据集名称
        StringBuilder sds_name = new StringBuilder();
        // 秩数
        int[] rank = new int[1];
        // 行列数
        int[] dimsizes = new int[2];
        // 数据类型
        int[] ntype = new int[1];
        // 属性数目
        int[] num_attrs = new int[1];
        // 读取数据集信息
        status = SDgetinfo(sds_id, sds_name, rank, dimsizes, ntype, num_attrs);
        if (status != -1)
        {
            row = dimsizes[0];
            column = dimsizes[1];
            // 读取数据集所有数据
            short[,] buffer = new short[row, column];
            status = SDreaddata(sds_id, new int[] { 0, 0 }, null, new int[] { row, column }, buffer);
            if (status != -1)
            {
                data = buffer;
            }
        }
        // 结束数据集访问
        status = SDendaccess(sds_id);
    }
    // 关闭HDF4文件
    status = SDend(sd_id);
}

从以上方法中可以看出,虽然HDF4官方并没有提供可直接让C#访问的类库,但我们还是可以基于DllImport这种静态函数引入的方式对其进行访问。

更多信息请参阅:The HDF Group – Information, Support, and Software

关于ArcGIS开发中计算两个几何对象之间距离的说明

在ArcGIS的二次开发中我们可以通过接口“IProximityOperator”的“ReturnDistance”方法来计算一个几何对象与另一个几何对象之间的距离。该接口被BezierCurve、CircularArc、EllipticArc、Envelope、GeoEllipse、GeoPolygon、GeoPolyline、MultiPatch、Multipoint、Point、Polygon、Polyline等几何对象实现。

// 定义用于计算的几何对象
IPoint point1 = new PointClass();
point1.X = 120;
point1.Y = 30;
// 定义计算距离的几何对象
IPoint point2 = new PointClass();
point2.X = 110;
point2.Y = 35;
// 获取IProximityOperator接口
IProximityOperator proximityOperator = point1 as IProximityOperator;
// 计算并返回几何对象point1到point2的距离
return proximityOperator.ReturnDistance(point2);