微软官方教程:https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/viewing-type-information

元編程(英語:Metaprogramming),又譯超編程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的資料,或者在运行时完成部分本应在编译时完成的工作。多数情况下,与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译。

编写元程序的语言称之为元语言。被操纵的程序的语言称之为「目标语言」。一门编程语言同时也是自身的元语言的能力称之为「反射」或者「自反」。

反射是促进元编程的一种很有价值的语言特性。把编程语言自身作为一級資料類型(如LISPForthRebol)也很有用。支持泛型编程的语言也使用元编程能力。

元编程通常通过两种方式实现。一种是通过应用程序编程接口(APIs)将运行时引擎的内部信息暴露于编程代码。另一种是动态执行包含编程命令的字符串表达式。因此,“程序能够编写程序”。虽然两种方式都能用于同一种语言,但大多数语言趋向于偏向其中一种。

个人理解元编程

C# 目前的反射只能对现在类型进行操作,还不能创建新的类型或编辑现有字段的类型信息。
反射获取的字段、属性、方法 如一个万能钥匙,可以用它操控所有的类型实例。C#泛型 反射都是元编程的能力

什么是反射

在程序运行时,动态获取 程序集, 类型(class,interface)和类型的成员信息(方法,字段,属性等)。
在程序运行时,动态创建 类型实例, 以及调用和方法 动态创建出来的 类型实例的成员。net core 一个程序运行起来以后,有一个AppDomain,在这个AppDomain中放了我们用到的所有assembly。

反射机制对应设计模式中的策略模式。

反射的作用:
1. 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现 有对象中获取类型
2. 应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射。
3. 反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能。框架(Spring .net/ .Net MVC等)

4、破坏单例模式 反射可以突破访问限制,直接调用私有构造函数

5、依赖注入

6、插件编程

7、序列化

C#反射的局限

反射不能修改readonly、只读属性、const字段、字符串拘留池  。

当修改readonly字段时候出现异常 System.FieldAccessException:“Cannot set initonly static field 'data' after type 'Program1' is initialized.”

修改只读属性的时候出现:Setting static readonly properties fails 异常。

using System.Reflection;

mod();

static void mod()
{
Program1 ss = new();
var fieldInfo = typeof(Program1).GetField("data", BindingFlags.Static | BindingFlags.NonPublic);
// fieldInfo.SetValue(ss, "MOD"); fieldInfo.SetValue(ss, "MOD");//出现异常 System.FieldAccessException:“Cannot set initonly static field 'data' after type 'Program1' is initialized.”
}
class Program1
{
static readonly string data = "df";
//
//这句运行正常 static string data = "df"; }

反射在在设计模式实现中的使用

  采用反射技术可以简化工厂的实现。 

  (1)工厂方法:通过反射可以将需要实现的子类名称传递给工厂方法,这样无须在子类中实现类的实例化。 

  (2)抽象工厂:使用反射可以减少抽象工厂的子类。 

  采用反射技术可以简化工厂代码的复杂程度,在.NET项目中,采用反射技术的工厂已经基本代替了工厂方法。 

  采用反射技术可以极大地简化对象的生成,对以下设计模式的实现也有很大影响。 

  (1)命令模式:可以采用命令的类型名称作为参数直接获得命令的实例,并且可以动态执行命令。 

  (2)享元模式:采用反射技术实例化享元可以简化享元工厂

反射用到命名空间

System.Reflection
    
System.Type
   
System.Reflection.Assembly
System.Reflection.Emit

System.Type 类:通过这个类可以访问任何给定数据类型的信息。
对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。


System.Reflection.Assembly类:它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。

System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。

System.Reflection


MemberInfo 包含以下类型:

MemberTypes

 [Flags]
public enum MemberTypes
{
Constructor = 1,
Event = 2,
Field = 4,
Method = 8,
Property = 16,
TypeInfo = 32,
Custom = 64,
NestedType = 128,
All = 191
}
}

TypeInfo和Type的区别与选择

Type 类:表示类型声明(类型的引用):类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型。。
TypeInfo 类:表示类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型的类型声明。

TypeInfo对象表示类型定义本身,而 Type 对象表示对类型定义的引用。 获取 TypeInfo 对象将强制加载包含该类型的程序集。 相比之下,你可以操作 Type 对象,而无需运行时加载它们引用的程序集。

TypeInfo出现于.net framework 4.5之后,这次调整用于区分两个概念:“reference”和“definition”。
    reference is a shallow representation of something
    definition is  a rich representation of something
例如System.Reflection.Assembly就代表了一个“definition” ,而System.Reflection.AssemblyName就代表了一个“reference”
在未区分这两种概念之前,System.Type是这两种概念的混合,通过一个Type实例,既可以获得该类型的“Name、Assembly、……”,也可以获得该类型的“NestTypes、Fields、Properties、Methods、Constructors、Events……”。这也导致了当我们获得一个Type的时候,就把它全部的信息都加载到了内存中,但是很多情况下,这并不是我们想要看到的。举例如下:
更多相关内容请查看:https://blog.csdn.net/fengsocool/article/details/85927995

public MyClass:BaseClass{

}

//获得MyClass的类型对象

Type t=MyClass.GetType();

在.net framework 4中,获得类型对象时,同时获得了基类(BaseClass)的类型对象,同时包括基类对象的“reference”和“definition”,因此需要把基类所在的程序集也加载进来。
在.net framework 4.5中,如下代码:
Type baseType = MyClass.GetType().GetTypeInfo().BaseType;
 在获得类型对象时,只获得了基类的"reference"

反射经常用方法

#define DEBUG  ////C# 的宏定义必须出现在所有代码之,告诉编译器哪些特性要编译IL代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection; namespace TestReflection
{
public delegate void NoParametMethodEventHandler(object o, EventArgs e);
public delegate void methodBing(string x); public class UnFinsedAttribute:Attribute
{ }
public interface IPlay { void Play(); }
[UnFinsed]
class MyType:IPlay
{ public event NoParametMethodEventHandler DianMing=delegate { };
Int32 myField;
//构造函数
public MyType()
{ }
public MyType(ref Int32 x) { x *= 5; }
public MyType(int x) { x = 5; }
public MyType(Int32 x, int c) { x *= 5; }
public MyType(Int32[] x, int c) { c *= 5; }
public override String ToString()=>myField.ToString();
public void Pritn(string x) => Console.WriteLine("Pritn(string x)");
public void Pritn(string x,int s) => Console.WriteLine("Pritn(string x,int s)");
//属性
public Int32 MyProp
{
get { return myField; }
set
{
if (value < 1)
throw new ArgumentOutOfRangeException("value", value, "value must be > 0");
myField = value;
}
} public string Address { get; set; }
public string Name => "自定义类"; //索引 public int this[string i]
{
get { return 1; }
set { }
}
public int this[int i]
{
get { return 1; }
set { }
}
//事件触发器
public void ClickMe()
{
DianMing += Huida;
DianMing.Invoke(new object(),new EventArgs());
}
//事件处理器
public void Huida(object o, EventArgs e)
{
Console.WriteLine($"{Name}到");
} public void Play()
{
Console.WriteLine("接口方法");
}
public void BingMethodToDelegat(string x)
{
Console.WriteLine($"你调用了动态方法BingMethodToDelegat方法的参数x:{x} 该方法调用了实例属性MyProp:{MyProp}");
}
public static void StaticMethod(string x)
{ Console.WriteLine("你调用了静态方法StaticMethod");
}
} class MyApp
{
static void Main()
{ Type type = typeof(MyType);
Type parameterType = typeof(int);
//====================构造函数获取 // Create an instance of a type. Object[] args = new Object[] { 8 };
object obj = Activator.CreateInstance(type);
//创建实例
Console.WriteLine("=========创建实例===============");
//创建泛型
Type open = typeof(Dictionary<,>);//创建泛型类型的实例,首先要获取对应的开放类型(Open type)的引用,然后调用Type类型的MakeGenericType方法
//传入一个包含泛型参数的数组即可获取一个封闭类型(Closed Type).使用该封闭类型,调用Activator接受Type参数的某个方法既可以构造出具体的实例。
Type closeType = open.MakeGenericType(typeof(String), typeof(object)); object obj1 = Activator.CreateInstance(closeType);//方法一、Activator创建实例
ConstructorInfo constructorCreature = type.GetConstructor(Array.Empty<Type>());
ConstructorInfo constructor6 = type.GetConstructor(new[] { typeof(int) });
var obj2 = constructor6.Invoke(new[]{(Object)3});//方法二、构造创建实例
Object obj3= type.Assembly.CreateInstance("MyType");//方法三、Assembly创建实例
Object obj4 = type.InvokeMember(null, BindingFlags.CreateInstance, null, null, new Object[] { 8 });//方法四、InvokeMember 创建实例 Type refParameter = parameterType.MakeByRefType(); //参数 MyType(ref Int32 x);
Type arrayParameter = parameterType.MakeArrayType(2); //参数数组int[]= new int[2];
Type parameterGeneric = typeof(List<>);//泛型参数
Type parameterGeneric2 = parameterGeneric.MakeGenericType(typeof(int));//list<int> //获取字段
Console.WriteLine("=========获取字段===============");
FieldInfo field = type.GetField("myField",BindingFlags.NonPublic|BindingFlags.Instance);
field.SetValue(obj, 1);//设置私有字段的
Console.WriteLine(field.GetValue(obj));//获取私有字段
Console.WriteLine(field.FieldType);//获取字段类型 //获取构造函数
Console.WriteLine("=========获取构造函数 ===========");
ConstructorInfo constructorInfo1 = type.GetConstructor(new Type[0]);//过时 获取无参构造函数 参数是无 new Type[0] 表示空数组
ConstructorInfo constructorInfo2 = type.GetConstructor(Array.Empty<Type>());//获取无参构造函数 参数是无 new Type[0] 表示空数组
ConstructorInfo constructorInfo3 = type.GetConstructor(new Type[] { });// 过时 获取无参构造函数 参数是无(new Type[] { }]表示空数组
ConstructorInfo constructorInfo4 = type.GetConstructor(Type.EmptyTypes); //获取无参构造函数 这个才是最正确符合逻辑的写法
ConstructorInfo constructorInfo5 = type.GetConstructor(new[] { refParameter });//MyType(ref Int32 x)
ConstructorInfo constructorInfo6 = type.GetConstructor(new[] { typeof(int) });//MyType(Int32 x)
ConstructorInfo constructorInfo7 = type.GetConstructor(new[] { typeof(int), typeof(int) });//MyType(Int32 x,int c)
ConstructorInfo constructorInfo8 = type.GetConstructor(new[] { typeof(int[]), typeof(int) });//MyType(Int32 x)
ConstructorInfo[] constructorInfoAll = type.GetConstructors();//获取所有构造函数 //获取属性
Console.WriteLine("========获取属性===============");
PropertyInfo propertyInfo1 = type.GetProperty("Name");
PropertyInfo propertyInfo2 = type.GetProperty("Address");
PropertyInfo[] propertyInfoAll = type.GetProperties();//获取所有的属性,把索引也获取进去了 索引是有参属性,名字item
Console.WriteLine(propertyInfo2.PropertyType);//属性的类型 Console.WriteLine(propertyInfo1.GetValue(obj));
// propertyInfo1.SetValue(obj, "haha");//错误的,因为没有set属性,所以无法设置,set就是set函数
Console.WriteLine(propertyInfo1.GetValue(obj)); Console.WriteLine(propertyInfo2.GetValue(obj));
propertyInfo2.SetValue(obj, "haha");//正确的,因为有set属性。
Console.WriteLine(propertyInfo2.GetValue(obj)); //获取索引
Console.WriteLine("=========获取索引===============");
PropertyInfo propertyInfo3 = type.GetProperty("Item",typeof(int),new[] { typeof(int) });//获取索引,索引的名字都是Item发,获取返回值是int,参数是int的属性。 //获取调用方法
Console.WriteLine("=========获取调用方法============");
MethodInfo method1 = type.GetMethod("ToString");
MethodInfo method2 = type.GetMethod("Pritn",new[] { typeof(string) });//获取一个参数的方法
MethodInfo method3 = type.GetMethod("Pritn",new[] {typeof(string),typeof(int)});//获取2个参数的方法
MethodInfo method4 = type.GetMethod("ClickMe");
MethodInfo method5 = type.GetMethod("Huida",new[] { typeof(object), typeof(EventArgs) });
MethodInfo method6 = type.GetMethod("StaticMethod", new[] { typeof(string) });//获取一个参数的方法 MemberInfo[] methodAll = type.GetMethods();
method4.Invoke(obj, null);//执行方法 //事件
Console.WriteLine("=========事件===============");
NoParametMethodEventHandler de = Huida2 ;
EventInfo @event = type.GetEvent("DianMing");//
@event.AddEventHandler(obj, de);//订阅事件
void Huida2(object o, EventArgs e) //处理器
{
Console.WriteLine("manin到");
}
method4.Invoke(obj, null);//执行方法
Console.WriteLine(@event.EventHandlerType.Name);//事件类型 //接口
Console.WriteLine("=========接口===============");
Type interFace = type.GetInterface("IPlay"); //委托绑定
Console.WriteLine("=========委托绑定===============");
Delegate methodDelegat1 = Delegate.CreateDelegate(typeof(methodBing), obj, "BingMethodToDelegat");//调用实例方法,必须绑定实例
methodDelegat1.DynamicInvoke("methodDelegat1");//实例方法要绑定实例使用,要不很容易报错,因为方法内部会调用很多内部成员 Delegate methodDelegat2 = Delegate.CreateDelegate(typeof(methodBing), null, method6);//调用了静态方法,静态不依附实例
methodDelegat2.DynamicInvoke("methodDelegat2"); //获取特性
var typeAttribute= type.GetCustomAttribute(typeof(UnFinsedAttribute)); } } }

反射为什么慢?

使用反射来调用类型或者触发方法,或者访问一个字段或者属性时clr 需要做更多的工作:校验参数,检查权限等等,所以速度是非常慢的。

【C#反射】开篇的更多相关文章

  1. 一年之计在于春,2015开篇:PDF.NET SOD Ver 5.1完全开源

    前言: 自从我2014年下半年到现在的某电商公司工作后,工作太忙,一直没有写过一篇博客,甚至连14年股票市场的牛市都错过了,现在马上要过年了,而今天又是立春节气,如果再不动手,那么明年这个无春的年,也 ...

  2. [.net 面向对象程序设计进阶] (1) 开篇

    [.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...

  3. 《InsideUE4》UObject(一)开篇

    UE生UObject,UObject生万物 引言 在上个GamePlay专题,谈到UE创建游戏世界的时候(GamePlay架构(一)Actor和Component),简单的介绍了一下UObject的功 ...

  4. .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引

    系列文章索引: .NET面试题解析(01)-值类型与引用类型 .NET面试题解析(02)-拆箱与装箱 .NET面试题解析(03)-string与字符操作 .NET面试题解析(04)-类型.方法与继承 ...

  5. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截

    程序猿修仙之路--数据结构之你是否真的懂数组?   数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少. ...

  6. java 反射和泛型

    反射 在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问.检测和修改它本身状态或行为的一种能力.[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为. 要注意术 ...

  7. 【Spring实战】----开篇(包含系列目录链接)

    [Spring实战]----开篇(包含系列目录链接) 置顶2016年11月10日 11:12:56 阅读数:3617 终于还是要对Spring进行解剖,接下来Spring实战篇系列会以应用了Sprin ...

  8. .netCore 反射 :Could not load file or assembly 系统找不到指定文件

    “System.IO.FileNotFoundException:“Could not load file or assembly 'ClassLibrary2, Culture=neutral, P ...

  9. 一个三年Android开发的总结-开篇

    一个三年Android开发的总结-开篇 转眼间全职从事Android开发已有三年,想把这一阶段的积累总结记录并展现出来,作为Android开发必备的知识,希望对有一定Android开发基础的人进阶有裨 ...

随机推荐

  1. CMake语法—普通变量与函数(Normal Variable And Function)

    目录 CMake语法-普通变量与函数(Normal Variable And Function) 1 CMake普通变量与函数示例 1.1 CMakeLists.txt 1.2 执行CMake配置脚本 ...

  2. Linux下进程线程,Nignx与php-fpm的进程线程方式

    1.进程与线程区别 进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集.从内核的观点看,进程的目的就是担当分配系统资源(CPU时间.内存等)的基本单位. 线程是进程的一个执行流, ...

  3. gin框架中的中间件

    全局中间件 所有请求都经过此中间件 中间件中设置值 func MiddleWare() gin.HandlerFunc { return func(context *gin.Context) { t ...

  4. gin中如何记录日志和错误日志

    package main import ( "github.com/gin-gonic/gin" "io" "os" ) func main ...

  5. 羽夏看Win系统内核—— APC 篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  6. Entity Framework 在OrderBy排序中使用字符串

    public static class LinqExtensions { private static PropertyInfo GetPropertyInfo(Type objType, strin ...

  7. JS 函数提升&变量提升以及函数声明&函数表达式的区别

    感谢原文作者:迟早会有猫 原文链接:https://www.cnblogs.com/SidselLoong/p/10515809.html 今天看js的变量提升问题,里面提到了函数提升.然后发现自己之 ...

  8. PHP中常见的数字掐头去尾操作方法

    四舍五入round round( float $val[, int $precision = 0[, int $mode = PHP_ROUND_HALF_UP]] ) : float 对浮点数进行四 ...

  9. 抽象修饰符abstract

    描述一类事物的时候发现该类存在一种行为,但不知道行为具体怎么实现,我们将这种行为称之为抽象行为,这时候我们就需要抽象类 抽象类的好处:抽象类中的抽象方法一定要备子类重写 抽象类注意点: 1.在类中,一 ...

  10. 鸟哥的Linux私房菜学习笔记——文件权限与目录配置

    Linux的文件权限和目录配置 在linux中的每个用户必需属于一个组,不能独立于组外.在linux中每个文件有所有者.所在组.其它组的概念. (1)所有者 一般为文件的创建者,谁创建了该文件,就是天 ...