字符串处理是我们最常用的功能,尤其是与其它类型的相互转也会经常使用。
1 Convert.ToInt32("23"); 2 Convert.ToDouble("3.4"); 3 Convert.ToDateTime("2014-4-4");
但是这个方法一旦出现转换失败将会非常影响程序的效率。
因此这种方式会更为合理:
1 int temp; 2 bool success= Int32.TryParse("23",out temp); 3 if(!success) 4 { 5 temp=0; 6 }
当然这种方式必须输入更多的代码,对于程序员来说有违人性,我也不想一个简单的转换就要输那么一堆代码。
宝宝表示很不高兴,于是定义了一个StringContainer类型以解决类似问题。
1 //能够存储基本类型 2 public void Test1() 3 { 4 StringContainer a= 1; 5 StringContainer b="2"; 6 StringContainer c=DateTime.Now; 7 } 8 //能够自动转换类型 9 public void Test2(StringContainer d) 10 { 11 StringContainer a="1"; 12 int b= a + 1; 13 DataTime c=a; 14 Console.Write(d); 15 } 16 //能够调用同一个函数,但参数类型可以不一样 17 public void Test3() 18 { 19 Test2(2); 20 Test2(1.2f); 21 Test2(DateTime.Now); 22 }
StringContainer类型的精简定义如下,只实现了Int32与String类型的互转:
1 public struct StringContainer 2 { 3 public static readonly StringContainer Empty=new StringContainer(); 4 public string value; 5 internal StringContainer(string value) 6 { 7 this.value = value; 8 } 9 public static implicit operator StringContainer(string value) 10 { 11 return new StringContainer(value); 12 } 13 public static implicit operator string(StringContainer stringContainar) 14 { 15 return stringContainar.value; 16 } 17 public static implicit operator Int32(StringContainer stringContainar) 18 { 19 Int32 result; 20 Int32.TryParse(stringContainar.value, out result); 21 return result; 22 } 23 public static implicit operator StringContainer(Int32 obj) 24 { 25 return new StringContainer(obj.ToString()); 26 } 27 }
而其中的StringContainer(string value)函数实现String类型到StringContainer类型的转换。
1 public static implicit operator StringContainer(string value) 2 { 3 return new StringContainer(value); 4 }
有了以上函数,我们就可以这样写:
StringContainer str = "23";
其中的Int32函数实现了String类型到Int32类型的自动转换。
1 public static implicit operator Int32(StringContainer stringContainar) 2 { 3 Int32 result; 4 Int32.TryParse(stringContainar.value, out result); 5 return result; 6 }
通过以上两个函数,我们可以这样写,是不是比Conert类和TryParse函数简单多了?
1 StringContainer str = "23"; 2 int num = str;
到底内部是怎样转换的呢?我们从IL上分析代码。
// Method begins at RVA 0x2130 // Code size 20 (0x14) .maxstack 1 .locals init ( [0] valuetype [NFinal4]System.StringContainer str, [1] int32 num ) IL_0000: nop IL_0001: ldstr "23" IL_0006: call valuetype [NFinal4]System.StringContainer [NFinal4]System.StringContainer::op_Implicit(string) IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: call int32 [NFinal4]System.StringContainer::op_Implicit(valuetype [NFinal4]System.StringContainer) IL_0012: stloc.1 IL_0013: ret
1.先将“23”推向栈顶。
2.然后调用以string为参数的初始化函数。
3.把str初始化的结果保存到本地变量中。
4.加载str到栈顶。
5.调用以StringContainer为参数的转换函数。
6.最后把结果存储到本地变量Num中。
从IL代码可以清楚了解。强制类型转换是由C#编译器自动完成的。
也就是说任意类型与StringContainer类型的相互转换是可以通过添加相应的转换函数实现的。
为了支持所有基本类型的相互转换,可以添加StringContainer.tt模板生成相应的类型转换函数。
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> using System; <#var nullableType=new string[]{"String"};#> <#var structType=new string[]{"SByte","Byte","Int16","UInt16","Int32","UInt32","Int64","UInt64","Boolean","Char","Decimal","Double","Single","DateTime","DateTimeOffset"};#> namespace System { public struct StringContainer { public static readonly StringContainer Empty=new StringContainer(); public string value; internal StringContainer(string value) { this.value = value; } public override bool Equals(object obj) { return base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); } public static bool operator ==(StringContainer container, string str) { return container.value == str; } public static bool operator !=(StringContainer container, string str) { return container.value != str; } <# for(int i=0;i<structType.Length;i++){#> public static implicit operator StringContainer(<#=structType[i]#> obj) { return new StringContainer(obj.ToString()); } <# }#> <# for(int i=0;i<structType.Length;i++){#> public static implicit operator StringContainer(<#=structType[i]#>? obj) { if(obj!=null) { return new StringContainer(obj.ToString()); } return StringContainer.Empty; } <# }#> public static implicit operator StringContainer(string value) { return new StringContainer(value); } public static implicit operator string(StringContainer stringContainar) { return stringContainar.value; } <#for(int i=0;i<structType.Length;i++){#> public static implicit operator <#=structType[i]#>(StringContainer stringContainar) { <#=structType[i]#> result; <#=structType[i]#>.TryParse(stringContainar.value, out result); return result; } <#}#> <#for(int i=0;i<structType.Length;i++){#> public static implicit operator <#=structType[i]#>?(StringContainer stringContainar) { <#=structType[i]#> result; if(<#=structType[i]#>.TryParse(stringContainar.value, out result)) { return result; } else { return null; } } <#}#> } }
实现这些基本函数后。我们可以这样写:
1 StringContainer str="23"; 2 int b=str*4; 3 str=DateTime.Now; 4 str.ToString(); 5 str=4.33; 6 double? a=str+1;
是不是感觉StringContainer有点var变量的味道?而且自带自动转换功能。
在之前的AspNet以及asp.net core中都有一个Context.Request.Form的Collection类型。
我们是否可以改造该类型呢?
于是我又定义了一个NameValueCollection类型
1 public class NameValueCollection : IEnumerable<KeyValuePair<string, StringContainer>> 2 { 3 public NameValueCollection() 4 { 5 collection = new Dictionary<string, StringContainer>(StringComparer.Ordinal); 6 } 7 private IDictionary<string, StringContainer> collection = null; 8 9 public StringContainer this[string key] 10 { 11 get { 12 if (collection.ContainsKey(key)) 13 { 14 return collection[key]; 15 } 16 else 17 { 18 return StringContainer.Empty; 19 } 20 } 21 set { 22 if (value.value==null) 23 { 24 if (collection.ContainsKey(key)) 25 { 26 collection.Remove(key); 27 } 28 } 29 else 30 { 31 if (collection.ContainsKey(key)) 32 { 33 collection[key] = value; 34 } 35 else 36 { 37 collection.Add(key, value); 38 } 39 } 40 } 41 } 42 public void Add(string key, string value) 43 { 44 this[key]=value; 45 } 46 47 public IEnumerator<KeyValuePair<string, StringContainer>> GetEnumerator() 48 { 49 return collection.GetEnumerator(); 50 } 51 52 public override string ToString() 53 { 54 StringWriter sw = new StringWriter(); 55 bool firstChild = true; 56 foreach (var item in collection) 57 { 58 if (firstChild) 59 { 60 firstChild = false; 61 } 62 else 63 { 64 sw.Write("&"); 65 } 66 sw.Write(item.Key); 67 sw.Write("="); 68 sw.Write(NFinal.Utility.UrlEncode(item.Value)); 69 } 70 return sw.ToString(); 71 } 72 73 IEnumerator IEnumerable.GetEnumerator() 74 { 75 return collection.GetEnumerator(); 76 } 77 }
这样的话,Form类型是否可以这样写?
int a=context.request.Form["a"];
byte? b=context.request.Form["b"];
float c=context.request.Form["c"];
DateTime d=context.request.Form["d"];
一个函数的参数类型与参数类型均不确定的函数如何定义?是不是想不到,那我们看看以下函数。
1 //函数 2 public void Run(params StringContainer[] scArray) 3 { 4 5 }
我们在调用时,仅仅需要instance.Run(1,"3",4.5,DateTime.Now);
当然也可以这样写:
1 public void Run(NameValueCollection nvc) 2 { 3 4 }
调用时:
1 NameValueCollection nvc=new NameValueCollection(); 2 nvc.Add("a",1); 3 nvc.Add("b",1.5); 4 nvc.Add("c",DateTime.Now); 5 instance.Run(nvc);
当然更进一步的话,我们可以利用.net 4.0的dynamic特性实现以下效果:
int a=context.request.Form.a;
byte? b=context.request.Form.b;
float c=context.request.Form.c;
DateTime d=context.request.Form.d;
具体思路就不在这里讲了。直接贴代码。
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Dynamic; namespace NFinal.Collections { public class NameValueDynamicCollection: DynamicObject, IDictionary<string, StringContainer> { private readonly IDictionary<string, StringContainer> _obj; public NameValueDynamicCollection() { _obj = new Dictionary<string, StringContainer>(); } public NameValueDynamicCollection(IDictionary<string, StringContainer> obj) { _obj = obj; } public StringContainer this[string key] { get { StringContainer result; if (_obj.TryGetValue(key, out result)) { return result; } else { return StringContainer.Empty; } } set { if (_obj.ContainsKey(key)) { _obj[key] = value; } else { _obj.Add(key, value); } } } [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "The compiler generates calls to invoke this")] public override bool TryGetMember(GetMemberBinder binder, out object result) { result = this[binder.Name]; return true; } [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "The compiler generates calls to invoke this")] public override bool TrySetMember(SetMemberBinder binder, object value) { if (value == null) { this[binder.Name] = StringContainer.Empty; } else { this[binder.Name] =new StringContainer(value.ToString()); } return true; } public void Add(string key, StringContainer value) { if (!_obj.ContainsKey(key)) { _obj.Add(key, value); } } public bool ContainsKey(string key) { return _obj.ContainsKey(key); } public ICollection<string> Keys { get { return _obj.Keys; } } public bool Remove(string key) { return _obj.Remove(key); } public bool TryGetValue(string key, out StringContainer value) { return _obj.TryGetValue(key, out value); } public ICollection<StringContainer> Values { get { return _obj.Values; } } public void Add(KeyValuePair<string, StringContainer> item) { _obj.Add(item); } public void Clear() { _obj.Clear(); } public bool Contains(KeyValuePair<string, StringContainer> item) { return _obj.Contains(item); } public void CopyTo(KeyValuePair<string, StringContainer>[] array, int arrayIndex) { _obj.CopyTo(array, arrayIndex); } public int Count { get { return _obj.Count; } } public bool IsReadOnly { get { return _obj.IsReadOnly; } } public bool Remove(KeyValuePair<string, StringContainer> item) { return _obj.Remove(item); } public IEnumerator<KeyValuePair<string, StringContainer>> GetEnumerator() { return _obj.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }
具体代码实现,请参看https://git.oschina.net/LucasDot/NFinal2