字符串类型的自动转换与识别

字符串处理是我们最常用的功能,尤其是与其它类型的相互转也会经常使用。

通常情况下我们需要String类型转其它的基础类型。这时我们最常用的是Convert类。
比如:
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

posted on 2017-03-01 14:26  老禄  阅读(1430)  评论(0编辑  收藏  举报

导航