有一天我在想反射的时候,突然间就想到了这个东西 就如同照镜子一样。镜子里面反射出了正在照镜子的人。 反射跟这个的效果就是一样的。通过反射我们程序就可以知道自己长什么样子的。 通过使用反射API 我们可以用访问到 程序集 模块 类型 与及类型上面的一些元信息 Attribute, 这些统称为元数据(metadata)。
我们还可以使用反射 动态的创建对象,并对对象的属性字段进行取值和赋值,也可以调用里面的方法,包括私有方法。
下面的示例当中我们将演示如何通过Attribute来读取CSV文件。并且CSV文件的列名跟我们定义的属性名不一致的情况。
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
namespace Malema.net
{
//定义一个我们自己的 CSV 特性Attribute
[AttributeUsage(AttributeTargets.Property)]
public class CsvAttribute : Attribute
{
public CsvAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
// 定义我们的股票数据类
public class Bar
{
[Csv("openprice")]
public float Open { get; set; }
[Csv("closeprice")]
public float Close { get; set; }
}
class Program
{
static async Task Main(string[] args)
{
//Csv的内容先处理成 下面的 dictionary形式,这边不展示出来了
var lines = new List<Dictionary<string, string>>();
lines.Add(new Dictionary<string, string> { ["openprice"] = "15", ["closeprice"] = "16" });
lines.Add(new Dictionary<string, string> { ["openprice"] = "16", ["closeprice"] = "17" });
var bars = GetRecords<Bar>(lines);
}
private static List<T> GetRecords<T>(List<Dictionary<string, string>> lines)
{
var list = new List<T>();
var properites = typeof(T).GetProperties(); // 得到所有的attribute
foreach (var line in lines)
{
var t = Activator.CreateInstance<T>(); // 创建一个实例
foreach (var item in properites)
{
var attribute = item.GetCustomAttribute<CsvAttribute>(); // 得到每一个属性的 attribute
var name = attribute.Name;
if (item.PropertyType == typeof(float)) //判断属性的类型是不是float
{
if (float.TryParse(line[name], out float value)) // 取出值。并转化成 float类型
{
item.SetValue(t, value);
}
}
}
list.Add(t);
}
return list;
}
}
}
bars的结果如下
这样做的缺点是性能不太好。性能比较好的方式。是用反射生成代码(Emit,或者构建一个类,然后编译它,加载进来)
在多态里面我们提过可以通过反射来自动创建类型。 如下。
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Malema.net
{
public interface IPay
{
string GetPayUrl();
}
public class AliPay : IPay
{
public string GetPayUrl()
{
// some code
return "AliPay Url";
}
}
public class WeChatPay : IPay
{
public string GetPayUrl()
{
// some code
return "WeChatPay Url";
}
}
class Program
{
static async Task Main(string[] args)
{
IPay pay = CreatePayByUser();
var url = pay.GetPayUrl();
Console.WriteLine(url); //"WeChatPay Url"
}
public static IPay CreatePayByUser()
{
var payType = typeof(IPay);
var types = payType.Assembly.GetTypes();
var payTypes = types.Where(x => x.IsAssignableTo(payType) && !x.IsInterface && x.IsClass).ToList();
var choosePayType = payTypes.FirstOrDefault(it => it.Name == "WeChatPay"); //"WeChatPay"这个是从参数当中获取的
return Activator.CreateInstance(choosePayType) as IPay;
}
}
}
有一些系统库 会把大部分的类加上Internal. 比如SqlClient。 这个时候我们先用它有public出来的类找到Assembly然后用完整的类名来获取它
var assembly = typeof(SqlConnection).Assembly;
var typeObject = assembly.GetType("Microsoft.Data.SqlClient.SqlConnectionFactory");