zoukankan      html  css  js  c++  java
  • 对泛型扩展方法进行反射的方法

    在使用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());
    		}
    	}
    
  • 相关阅读:
    练习题
    java关键字
    循环结构
    第一天的学习
    爬虫的简单介绍
    Windows10 专业版秘钥激活
    flask补充
    Flask框架
    小程序登录、授权、支付
    赃读、不可重复读 和 幻读
  • 原文地址:https://www.cnblogs.com/asis/p/6892196.html
Copyright © 2011-2022 走看看