對泛型擴展方法進行反射的方法


在使用dapper時,都用IConnection上有一個Query<T>的函數。我們項目組的成員提出了一個問題:我不知道怎么去調用它?
為了demo這個情形,我打算在string類上寫個擴展方法。這個方法假設叫做IsOK。
我們先分解一下需求:

  • 對string類擴展方法,加入IsOK<T>(T b)和他的一個重載IsOK<T1,T2>(T1 a, T2 b)
  • 調用使用反射的方式調用IsOK<T1,T2>(T1 a, T2 b)函數
  • T1, T2要能夠動態的變換類型。例如我可以傳入參數 int, byte,就會調用 IsOK<int, byte>(int a, byte b),下面會詳解這個需求。

對string類擴展方法

我們先做分解動作,第一步,對string類擴展方法,這個全世界的c#程序員都能做。

public static class StringExtension
{
	public static bool IsOK(this string a, string b)
	{
		return a.StartsWith(b[0].ToString());
	}

	public static bool IsOK<T>(this string a, T b)
	{
		return a.StartsWith(b.ToString());
	}

	public static bool IsOK<T1, T2>(this string a, T1 b, T2 c)
	{
		return a.StartsWith(b.ToString());
	}
}

反射IsOK<T1,T2>(T1 a, T2 b)函數

這里有一點值得說的是,反射泛型需要用到你的那個Extionsion類。也就是說,我們對string類進行擴展了,但是我們在反射的時候,並不能使用string類,應該使用我們自己寫的StringExtension類。
然后,我們這里如何來判斷是哪個重載呢?在這里,因為我的參數個數不一樣IsOK<T1, T2>(this string a, T1 b, T2 c),我可以簡單的判斷參數的個數如果是3個,就是我想要的結果,注意IsOK<T1, T2>(this string a, T1 b, T2 c)的參數是3個哦!
千萬不要認為調用時,好像只提供了2個參數。其實這里是3個。
那么
var mi = typeof(StringExtension).GetMethods().FirstOrDefault(m => m.IsGenericMethod && m.Name == "IsOK" && m.GetParameters().Length == 3);
這樣的一句代碼,就能幫我找到我想要的泛型。
余下的,我只需要MakeGenericMethod就能完成調用前的准備工作。

動態的變換類型

我設想這樣一個場景,如果我有這樣一個函數

static void Foo(string typename1, string typename2)
{
	// TODO: need implement generic types.
}

我希望我可以這樣調用:
Foo("int","float") 這樣就可以調用到 string.IsOK(int a, float b)。同理:
Foo("double","string") => string.IsOK(double a, string b)
Foo("MyClass1","MyClass2) => string.IsOK(MyClass1 a, MyClass2 b)
為什么要這樣做呢?
你再設想一下這個場景,如果程序是客戶端+服務器端的程序,在客戶端那邊想調用string.IsOK<string, IntPtr>(string a, IntPtr b)string.IsOK<double, double>(double a, double b),那你應該怎么樣做呢?是不是動態的反射要方便一點?我們只需要把字符串的"string"轉換成string類型,把"dobule"轉換成double就行了。

static Tuple<object, object> Foo(string typename1, string typename2)
{
	var p1 = Activator.CreateInstance(Type.GetType(typename1));
	var p2 = Activator.CreateInstance(Type.GetType(typename2));
	return new Tuple<object, object>(p1, p2);
}

大的問題應該已經解決。

完整程序

在程序里面,我們可以通過改變sp1和sp2的類型來調用這個泛型。

class Program
	{
		static void Main(string[] args)
		{
			var mi = typeof(StringExtension).GetMethods().FirstOrDefault(m => m.IsGenericMethod && m.Name == "IsOK" && m.GetParameters().Length == 3);

			var h = "hello world";
			var sp1 = "System.Int32";
			var sp2 = "System.Double";

			var t = Foo(sp1, sp2);

			var mi2 = mi.MakeGenericMethod(Type.GetType(sp1), Type.GetType(sp2));

			mi2.Invoke(null, new object[] { h, t.Item1, t.Item2 });

			// h.IsOK(p1, p2);
			Console.Read();
		}

		static Tuple<object, object> Foo(string typename1, string typename2)
		{
			var p1 = Activator.CreateInstance(Type.GetType(typename1));
			var p2 = Activator.CreateInstance(Type.GetType(typename2));
			return new Tuple<object, object>(p1, p2);
		}
	}

	public static class StringExtension
	{
		public static bool IsOK(this string a, string b)
		{
			return a.StartsWith(b[0].ToString());
		}

		public static bool IsOK<T>(this string a, T b)
		{
			return a.StartsWith(b.ToString());
		}

		public static bool IsOK<T1, T2>(this string a, T1 b, T2 c)
		{
			return a.StartsWith(b.ToString());
		}
	}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM