利用 TypeConverter,转换字符串和各种类型只需写一个函数
本文代码基于 .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;
}
这里就郁闷了,因为虽然方法内部的实现都长得差不多,但他们之间除了名字相同之外(比如 Parse
和ToString
),并没有什么联系;这样,便不能使用统一的接口或者抽象类等等来调用。
尝试使用反射
既然名字类似,那自然又能想到反射。可是,拥有 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 中能够通过字符串编写的类型均可以通过这种方式进行转换。然而,目前我还为对此进行验证。
验证猜想
- 去 https://referencesource.microsoft.com/ 看
TypeDescriptor.GetConverter
的源码(点击进入)。 - 尝试自定义一个类型,在类型上标记
TypeConverter
,并对此类进行测试。
利用 TypeConverter,转换字符串和各种类型只需写一个函数的更多相关文章
- linux上怎么切换不同版本的arm-linux-gcc?只需改一行函数
linux上怎么切换不同版本的arm-linux-gcc?只需改一行函数 ln -s /usr/local/arm/3.4.1/bin/arm-linux-gcc /usr/bin/arm-linux ...
- 【C语言】写一个函数,实现字符串内单词逆序
//写一个函数,实现字符串内单词逆序 //比如student a am i.逆序后i am a student. #include <stdio.h> #include <strin ...
- RecyclerView.Adapter封装,最简单实用的BaseRecyclerViewAdapter;只需重写一个方法,设置数据链式调用;
之前对ListView的BaseAdapter进行过封装,只需重写一个getView方法: 现在慢慢的RecyclerView成为主流,下面是RecyclerView.Adapter的封装: Base ...
- 38 写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度。
题目:写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度. public class _038PrintLength { public static void main(Stri ...
- 写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度
import java.util.Scanner; /** * [程序38] * * 题目:写一个函数,求一个字符串的长度,在main函数中输入字符串,并输出其长度. * * @author Jame ...
- 写一个函数,输入int型,返回整数逆序后的字符串
刚刚看到一个面试题:写一个函数,输入int型,返回整数逆序后的字符串.如:输入123,返回"321". 要求必须用递归,不能用全局变量,输入必须是一个參数.必须返回字符串.&quo ...
- C++ 利用指针和数组以及指针和结构体实现一个函数返回多个值
C++ 利用指针和数组实现一个函数返回多个值demo1 #include <iostream> using namespace std; int* test(int,int,int); i ...
- 写一个函数,实现两个字符串的比较。即实现strcmp函数,s1=s2时返回0,s1!=s2时返回二者第一个不同字符的ASCII值。
#include<stdio.h> #include<stdlib.h> int main(){ setvbuf(stdout,NULL,_IONBF,); ],s2[]; i ...
- Java实现汉诺塔移动,只需传一个int值(汉诺塔的阶)
public class HNT { public static void main(String[] args) { HNT a1 = new HNT(); a1.lToR(10); //给汉诺塔a ...
随机推荐
- 关于javascript以及jquery如何打开文件
其实很简单, <input type="file" id="file" mce_style="display:none"> 这个 ...
- 使用MessageFormat替换字符中的占位符
使用String.format可以实现字符串的格式化功能,即将后面参数中的值替换掉format中的%s,%d这些值.但MessageFormat更为强大,不用管传入值是字符串还是数字,使用占位符即可. ...
- RabbitMQ 与 AMQP路由
概述 RabbitMQ(MQ 为 MessageQueue) 是一个消息队列,主要是用来实现应用程序的异步和解耦,同时起到消息缓冲.消息分发作用 消息队列 消息(Message)是指应用间传送的数据, ...
- Java Applet基础
applet是一种Java程序.它一般运行在支持Java的Web浏览器内.因为它有完整的Java API支持,所以applet是一个全功能的Java应用程序. 如下所示是独立的Java应用程序和app ...
- hdu4305生成树计数
先预处理出距离,然后判断是否可行,要注意判断是否在一条直线上时判断是在两侧还是一边(wa了四次) double型数据 #include<map> #include<set> # ...
- Pytorch入门笔记
import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): #nn. ...
- Ansible 小手册系列 十一(变量)
变量名约束 变量名称应为字母,数字和下划线. 变量应始终以字母开头. 变量名不应与python属性和方法名冲突. 变量使用 通过命令行传递变量(extra vars) ansible-playbook ...
- vue-router防跳墙控制
vue-router防跳墙控制 因为在实际开发中,从自己的角度来看,发现可以通过地址栏输入地址,便可以进入本没有权限的网页.而我们一般只是操作登录页面,其他页面很少考虑,此刻特来尝试解决一下 基于vu ...
- C#学习历程(六)[ref 关键字的使用]
ref 关键字的使用 ref 关键字通过引用(而非值)传递参数. 通过引用传递的效果是,对所调用方法中的参数进行的任何更改都反映在调用方法中. 例如,如果调用方传递本地变量表达式或数组元素访问表达式, ...
- 缓存LruCache简单创建和使用
LruCache一般使用: /** * 总容量为当前进程的1/8,单位:KB * sizeOf():计算缓存对象的大小,单位要一致 * entryRemoved():移除旧缓存时调用 */ int m ...