本文代码基于 .NET Framework 实现。

本来只想进行简单的配置存储的,不料发现 .NET 的基本类型多达十多种。于是,如果写成下面这样,那代码可就太多了哦:

// 注:`Configurator`是我的配置类,用于读写字符串的。
public static int GetInt32(this Configurator config, string key)
{
return int.Parse(config[key], CultureInfo.InvariantCulture);
}
public static void SetInt32(this Configurator config, string key, int value)
{
config[key] = value.ToString(CultureInfo.InvariantCulture);
} public static bool GetBoolean(this Configurator config, string key)
{
return bool.Parse(config[key]);
}
// 还没完,这才 1.5 种而已。
// ……

尝试使用泛型

这些方法都比较相似,于是自然而然想到了泛型,所以写出了这段代码:

public static T GetValue<T>(this Configurator config, string key) where T : struct
{
var @string = config[key];
// T value = 某种通用的转换(@string); // 问题来了,这里该怎么写?
return value;
}

这里就郁闷了,因为虽然方法内部的实现都长得差不多,但他们之间除了名字相同之外(比如 ParseToString),并没有什么联系;这样,便不能使用统一的接口或者抽象类等等来调用。

尝试使用反射

既然名字类似,那自然又能想到反射。可是,拥有 Parse 的类型并不多,ToString 中能传入 CultureInfo.InvariantCulture 且参数顺序保持一致的类型也少的可怜。于是,反射只能保证写得出来代码,却并不能保证多种类型的支持。

另外想到一点,Int32 类型的 TryParse 中有 out 关键字修饰的参数,反射能否支持呢?StackOverflow 上找到了答案

You invoke a method with an out parameter via reflection just like any other method. The difference is that the returned value will be copied back into the parameter array so you can access it from the calling function.

object[] args = new object[] { address, request };
_DownloadDataInternal.Invoke(this, args);
request = (WebRequest)args[1];

意思是说,这在反射中不用作什么考虑,传入的参数数组天然就已经支持了 out 关键字。

尝试寻找更通用的方案

在 Google 上继续漫游,在 StackOverflow 上发现这篇讨论:How to convert string to any type

最高票答案给出的回复是:

using System.ComponentModel;

TypeConverter typeConverter = TypeDescriptor.GetConverter(propType);
object propValue = typeConverter.ConvertFromString(inputValue);

这可打开了思路,原来 .NET Framework 内部已经有了这种转换机制和相关的方法。于是用这种方法修改我的方法,成了这样子:

public static T GetValue<T>(this Configurator config, string key) where T : struct
{
var @string = config[key];
var td = TypeDescriptor.GetConverter(typeof (T));
return (T) td.ConvertFromInvariantString(@string);
}
public static void SetValue<T>(this Configurator config, string key, T value) where T : struct
{
var td = TypeDescriptor.GetConverter(typeof (T));
var @string = td.ConvertToInvariantString(value);
config[key] = @string;
}

编写单元测试发现,这种方法能够支持的类型真的非常多,byte char short ushort int uint long ulong bool float double decimal DateTime Point Vector Size Rect Matrix Color……

看看代码中 TypeDescriptor.GetConverter 的返回值发现是 TypeConverter 类型的,而我们在 WPF 的 xaml 中编写自定义类型时,经常需要执行此类型的 TypeConverter。凭这一点可以大胆猜测,xaml 中能够通过字符串编写的类型均可以通过这种方式进行转换。然而,目前我还为对此进行验证。

验证猜想

  1. 去 https://referencesource.microsoft.com/ 看 TypeDescriptor.GetConverter 的源码(点击进入)。
  2. 尝试自定义一个类型,在类型上标记 TypeConverter,并对此类进行测试。

利用 TypeConverter,转换字符串和各种类型只需写一个函数的更多相关文章

  1. linux上怎么切换不同版本的arm-linux-gcc?只需改一行函数

    linux上怎么切换不同版本的arm-linux-gcc?只需改一行函数 ln -s /usr/local/arm/3.4.1/bin/arm-linux-gcc /usr/bin/arm-linux ...

  2. 【C语言】写一个函数,实现字符串内单词逆序

    //写一个函数,实现字符串内单词逆序 //比如student a am i.逆序后i am a student. #include <stdio.h> #include <strin ...

  3. RecyclerView.Adapter封装,最简单实用的BaseRecyclerViewAdapter;只需重写一个方法,设置数据链式调用;

    之前对ListView的BaseAdapter进行过封装,只需重写一个getView方法: 现在慢慢的RecyclerView成为主流,下面是RecyclerView.Adapter的封装: Base ...

  4. 38 写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。

    题目:写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度. public class _038PrintLength { public static void main(Stri ...

  5. 写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度

    import java.util.Scanner; /** * [程序38] * * 题目:写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度. * * @author Jame ...

  6. 写一个函数,输入int型,返回整数逆序后的字符串

    刚刚看到一个面试题:写一个函数,输入int型,返回整数逆序后的字符串.如:输入123,返回"321". 要求必须用递归,不能用全局变量,输入必须是一个參数.必须返回字符串.&quo ...

  7. C++ 利用指针和数组以及指针和结构体实现一个函数返回多个值

    C++ 利用指针和数组实现一个函数返回多个值demo1 #include <iostream> using namespace std; int* test(int,int,int); i ...

  8. 写一个函数,实现两个字符串的比较。即实现strcmp函数,s1=s2时返回0,s1!=s2时返回二者第一个不同字符的ASCII值。

    #include<stdio.h> #include<stdlib.h> int main(){ setvbuf(stdout,NULL,_IONBF,); ],s2[]; i ...

  9. Java实现汉诺塔移动,只需传一个int值(汉诺塔的阶)

    public class HNT { public static void main(String[] args) { HNT a1 = new HNT(); a1.lToR(10); //给汉诺塔a ...

随机推荐

  1. H5唤起app

    H5唤起app 1.判断是否在微信中打开 无论是在哪个平台的客户端Android/IOS,在微信的平台上访问都有一个问题,那就是无法启动客户端,这是微信为了安全性考虑的限制,android这边屏蔽sc ...

  2. [Vue]使用 vue-i18n 切换中英文

    1.引入 vue-i18n  import Vue from 'vue' import VueI18n from 'vue-i18n' import merge from 'lodash/merge' ...

  3. WPF TextBox 获得焦点后,文本框中的文字全选中

    textbox.GotFocus 事件处理 Textbox.SelectAll() 是不行的, 这样处理会发生的情况是: 1) textbox1 当前没有焦点, 内容为 someText. 2) 鼠标 ...

  4. CAJ2PDF

    该项目不成熟,很容易遇到转换失败的例子. https://github.com/JeziL/caj2pdf https://github.com/JeziL/caj2pdf/wiki caj2pdf ...

  5. table 转实体

    public class Table2Entity<T> where T : class,new() { public static List<T> GetEntitys(Da ...

  6. iOS安全系列之 HTTPS

    作者:Jaminzzhang 如何打造一个安全的App?这是每一个移动开发者必须面对的问题.在移动App开发领域,开发工程师对于安全方面的考虑普遍比较欠缺,而由于iOS平台的封闭性,遭遇到的安全问题相 ...

  7. 使用XMLHttpRequest对象完成原生的AJAX请求

    1.大家眼中的Ajax 说到Ajax,只要有过前端开发经验的童鞋一定都不陌生,大都知道它就是一种与后端之间的通信技术,通过这个神奇的家伙,我们不用像传统表单那样填完信息一点提交就呼啦呼啦跳转了.Aja ...

  8. NOI Linux下Emacs && gdb调试方法

    1. 首先要配置emacs文件: (global-linum-mode t) (show-paren-mode t) (global-set-key (kbd "C-s") 'sa ...

  9. webapi在IIS发布后报Http 403.14 error

    服务器是Windows Server 2008 R2 Enterprise IIS6.1     解决方法,修改web.config文件   1.在<system.webServer>配置 ...

  10. poj2195

    题解: 简单KM 把每一个男的和房子分离 代码: #include<cstdio> #include<cmath> #include<algorithm> #inc ...