本文代码基于 .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. 通过ajax提交到url路由

    $regBoxform.find('button').on('click', function(){ /*通过ajax提交请求*/ $.ajax({ type:'post', /*用post 方式提交 ...

  2. solr 5.1.0安装-Windows(亦可用于5.4.1)

    以前4.10的时候写过一个安装教程,是安装在tomcat的,在来安装5.1的时候,看了下简介,发现从5.x后solr集成了jetty,安装变得简单了不少. 现在只需要三步就能搞定,下载solr包解压, ...

  3. jQuery回车键快速切换下一个input文本框解决方案

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. C#多线程3种创建Thread、Delegate.BeginInvoke、线程池

    1   创建多线程,一般情况有以下几种:(1)通过Thread类   (2)通过Delegate.BeginInvoke方法   (3)线程池 using System; using System.C ...

  5. 转载:Javascript面向对象编程原理 -- 理解对象

    源地址:http://www.html-js.com/article/1717 虽然JavaScript中已经自带了很多内建引用类型,你还是会很频繁的需要创建自己的对象.JavaScript编程的很大 ...

  6. 51nod-1574-排列转换

    1574 排列转换  题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 现在有两个长度为n的排列p和s.要求通过交换 ...

  7. hdu 4679 Terrorist’s destroy 树的直径+dp

    题意:给你一棵树,每条边都有值W,然后问你去掉一条边,令val = w*max(两颗新树的直径),求val最小值~ 做法,先求树的直径,然后算出直径上每个点的最长枝条长度.这样对于每一条边,假如是枝条 ...

  8. zk的watcher机制的实现

    转载:https://www.ibm.com/developerworks/cn/opensource/os-cn-apache-zookeeper-watcher/ http://blog.csdn ...

  9. angularJS---初识指令

    angularJS 什么是angularJS AngularJS 诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款 ...

  10. 【hive】子查询

    hive中是不支持子查询的 但是并不意味这不支持in 或者 not in in 或者not in 后边是定值的话是支持的 但是接定制是可以的 例如 select id from table not i ...