C#_Extension Method技巧

 

C#預設有些函數功能在使用上可能會覺得有些微不足
因此偶而會想自行做擴充

擴充方法可讓您在現有類型中「加入」方法,而不需要建立新的衍生類型、重新編譯,或是修改原始類型。

別於自行定義的function  ,其可讓我們在現有的內建物件中做擴展方法。

可以在專案新增一個Extention目錄並額外新增一個class取名為CustomExtention
用public static修飾 裡面自行建立的各種method也必須是public static


Case.1 兩個Dictionary Merge的功能擴充
我想要實作Dictionary A 去merge Dictionary B返回一個合併的Dictionary的功能
由於.net Dictionary本身型別內建沒提供該功能


CustomExtension.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp.Extension
{
    public static class CustomExtension
    {
		public static Dictionary<Key, Value> MergeDic<Key, Value>(this Dictionary<Key, Value> left, Dictionary<Key, Value> right)
		{
			if (left == null)
			{
				throw new ArgumentNullException("Can't merge into a null dictionary");
			}
			else if (right == null)
			{
				return left;
			}

			foreach (var kvp in right)
			{
				if (!left.ContainsKey(kvp.Key))
				{
					left.Add(kvp.Key, kvp.Value);
				}
			}

			return left;
		}

	}
}



外部在使用時候引用該命名空間
using ExtensionMethodApp.Extension;

Program.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using ExtensionMethodApp.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, string> dicA = new Dictionary<string, string>();
            dicA.Add("A", "Almond");
            dicA.Add("B", "Berry");

            Dictionary<string, string> dicB = new Dictionary<string, string>();
            dicA.Add("C", "Cherry");
            dicA.Add("D", "Damson");

            Dictionary<string,string> dicRes = dicA.MergeDic(dicB);

            Console.ReadKey();
        }
    }
}







Case.2 判斷字串內文是否為Json格式的String 擴充
這裡我們補安裝完Newtonsoft.Json並引入相應namespace
於CustomExtension新增程式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp.Extension
{
    public static class CustomExtension
    {
		public static Dictionary<Key, Value> MergeDic<Key, Value>(this Dictionary<Key, Value> left, Dictionary<Key, Value> right)
		{
			if (left == null)
			{
				throw new ArgumentNullException("Can't merge into a null dictionary");
			}
			else if (right == null)
			{
				return left;
			}

			foreach (var kvp in right)
			{
				if (!left.ContainsKey(kvp.Key))
				{
					left.Add(kvp.Key, kvp.Value);
				}
			}

			return left;
		}

        /// <summary>
        /// Determine input string is json format
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsJsonFormat(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return false;

            if ((value.StartsWith("{") && value.EndsWith("}")) ||
                (value.StartsWith("[") && value.EndsWith("]")))
            {
                try
                {
                    var obj = JToken.Parse(value);
                    return true;
                }
                catch (JsonReaderException)
                {
                    return false;
                }
            }
            return false;
        }



    }
}



在外部調用測試是否為json格式時候就可以這樣子來判斷

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using ExtensionMethodApp.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string str_json = "";
            if (str_json.IsJsonFormat())
            {

            }
            else
            {
                str_json = "{'name':'John', 'age':30, 'car':null}";
                if (str_json.IsJsonFormat())
                {

                }
            }
            Console.ReadKey();
        }
    }
}





Case.3 Enum想要取實際的字串值定義而非原本的數字
實務上enum會因為相關的資料而去特別定義再一起的資料結構
然而直接透過enum預設數值編號不太容易識別
也會希望直接取得enum對應的字串值

C# Enum中我們可以自定義客製的attribute標記在enum項目上
比方像DescriptionAttribute

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using ExtensionMethodApp.Extension;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp
{
    class Program
    {
        public enum ContentTypeOption
        {
            [Description("application/x-www-form-urlencoded")]
            form_urlencoded,
            [Description("application/json")]
            json_format,
            [Description("application/xml")]
            xml_format,
            [Description("application/exchange+xml")]
            ex_xml_format,
            [Description("application/pdf")]
            pdf_format,
            [Description("application/msword")]
            msword_format,
            [Description("multipart/form-data")]
            multipart_form_data,
            [Description("text/plain")]
            text_plain,
            [Description("text/html")]
            text_html,
            [Description("text/xml")]
            text_xml,
            [Description("image/gif")]
            image_gif,
            [Description("image/png")]
            image_png,
            [Description("image/jpeg")]
            image_jpeg,
        }

        static void Main(string[] args)
        {
            ContentTypeOption contentTypeOption = ContentTypeOption.image_jpeg;            

            Console.ReadKey();
        }
    }
}



但功能仍不太足夠
因為我們想要去取得該字串描述的實質定義

此時我們可先去自己建立客製的Attribute

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp.Extension
{
    public class StringValueAttribute : Attribute
    {
        public string StringValue { get; protected set; }

        public StringValueAttribute(string value)
        {
            this.StringValue = value;
        }
    }
}


之後再添增於enum項目中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using ExtensionMethodApp.Extension;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp
{
    class Program
    {
        public enum ContentTypeOption
        {
            [StringValueAttribute("application/x-www-form-urlencoded")]
            form_urlencoded,
            [StringValueAttribute("application/json")]
            json_format,
            [StringValueAttribute("application/xml")]
            xml_format,
            [StringValueAttribute("application/exchange+xml")]
            ex_xml_format,
            [StringValueAttribute("application/pdf")]
            pdf_format,
            [StringValueAttribute("application/msword")]
            msword_format,
            [StringValueAttribute("multipart/form-data")]
            multipart_form_data,
            [StringValueAttribute("text/plain")]
            text_plain,
            [StringValueAttribute("text/html")]
            text_html,
            [StringValueAttribute("text/xml")]
            text_xml,
            [StringValueAttribute("image/gif")]
            image_gif,
            [StringValueAttribute("image/png")]
            image_png,
            [StringValueAttribute("image/jpeg")]
            image_jpeg,
        }

        static void Main(string[] args)
        {
            ContentTypeOption contentTypeOption = ContentTypeOption.image_jpeg;            

            Console.ReadKey();
        }
    }
}


接著進行Extension Method的擴充設計
由於有運用到Reflection的api因此
using System.Reflection;記得引入

在此擴充GetStringValue
用於直接取得enum item的string attribute

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethodApp.Extension
{
    public static class CustomExtension
    {
        /// <summary>
        /// Will get the string value for a given enums value, this will
        /// only work if you assign the StringValue attribute to
        /// the items in your enum.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetStringValue(this Enum value)
        {
            Type type = value.GetType();
            FieldInfo fieldInfo = type.GetField(value.ToString());
            StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
            return attribs.Length > 0 ? attribs[0].StringValue : null;
        }


        public static Dictionary<Key, Value> MergeDic<Key, Value>(this Dictionary<Key, Value> left, Dictionary<Key, Value> right)
		{
			if (left == null)
			{
				throw new ArgumentNullException("Can't merge into a null dictionary");
			}
			else if (right == null)
			{
				return left;
			}

			foreach (var kvp in right)
			{
				if (!left.ContainsKey(kvp.Key))
				{
					left.Add(kvp.Key, kvp.Value);
				}
			}

			return left;
		}

        /// <summary>
        /// Determine input string is json format
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsJsonFormat(this string value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return false;

            if ((value.StartsWith("{") && value.EndsWith("}")) ||
                (value.StartsWith("[") && value.EndsWith("]")))
            {
                try
                {
                    var obj = JToken.Parse(value);
                    return true;
                }
                catch (JsonReaderException)
                {
                    return false;
                }
            }
            return false;
        }



    }
}



而在外部調用時候就能成功把string 屬性的定義取出









擴充方法 (C# 程式設計手冊)
https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/classes-and-structs/extension-methods














留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header