【C#反射】开篇
微软官方教程:https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/viewing-type-information
元編程(英語:Metaprogramming),又譯超編程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的資料,或者在运行时完成部分本应在编译时完成的工作。多数情况下,与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译。
编写元程序的语言称之为元语言。被操纵的程序的语言称之为「目标语言」。一门编程语言同时也是自身的元语言的能力称之为「反射」或者「自反」。
反射是促进元编程的一种很有价值的语言特性。把编程语言自身作为一級資料類型(如LISP、Forth或Rebol)也很有用。支持泛型编程的语言也使用元编程能力。
元编程通常通过两种方式实现。一种是通过应用程序编程接口(APIs)将运行时引擎的内部信息暴露于编程代码。另一种是动态执行包含编程命令的字符串表达式。因此,“程序能够编写程序”。虽然两种方式都能用于同一种语言,但大多数语言趋向于偏向其中一种。
个人理解元编程
C# 目前的反射只能对现在类型进行操作,还不能创建新的类型或编辑现有字段的类型信息。
反射获取的字段、属性、方法 如一个万能钥匙,可以用它操控所有的类型实例。C#泛型 反射都是元编程的能力
什么是反射
反射机制对应设计模式中的策略模式。
反射的作用:
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#反射】开篇的更多相关文章
- 一年之计在于春,2015开篇:PDF.NET SOD Ver 5.1完全开源
前言: 自从我2014年下半年到现在的某电商公司工作后,工作太忙,一直没有写过一篇博客,甚至连14年股票市场的牛市都错过了,现在马上要过年了,而今天又是立春节气,如果再不动手,那么明年这个无春的年,也 ...
- [.net 面向对象程序设计进阶] (1) 开篇
[.net 面向对象程序设计进阶] (1) 开篇 上一系列文章<.net 面向对象编程基础>写完后,很多小伙伴们希望我有时间再写一点进阶的文章,于是有了这个系列文章.这一系列的文章中, 对 ...
- 《InsideUE4》UObject(一)开篇
UE生UObject,UObject生万物 引言 在上个GamePlay专题,谈到UE创建游戏世界的时候(GamePlay架构(一)Actor和Component),简单的介绍了一下UObject的功 ...
- .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引
系列文章索引: .NET面试题解析(01)-值类型与引用类型 .NET面试题解析(02)-拆箱与装箱 .NET面试题解析(03)-string与字符操作 .NET面试题解析(04)-类型.方法与继承 ...
- 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截
程序猿修仙之路--数据结构之你是否真的懂数组? 数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构 .要想在之后的江湖历练中通关,数据结构必不可少. ...
- java 反射和泛型
反射 在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问.检测和修改它本身状态或行为的一种能力.[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为. 要注意术 ...
- 【Spring实战】----开篇(包含系列目录链接)
[Spring实战]----开篇(包含系列目录链接) 置顶2016年11月10日 11:12:56 阅读数:3617 终于还是要对Spring进行解剖,接下来Spring实战篇系列会以应用了Sprin ...
- .netCore 反射 :Could not load file or assembly 系统找不到指定文件
“System.IO.FileNotFoundException:“Could not load file or assembly 'ClassLibrary2, Culture=neutral, P ...
- 一个三年Android开发的总结-开篇
一个三年Android开发的总结-开篇 转眼间全职从事Android开发已有三年,想把这一阶段的积累总结记录并展现出来,作为Android开发必备的知识,希望对有一定Android开发基础的人进阶有裨 ...
随机推荐
- [源码分析] Facebook如何训练超大模型 --- (3)
[源码分析] Facebook如何训练超大模型 --- (3) 目录 [源码分析] Facebook如何训练超大模型 --- (3) 0x00 摘要 0x01 ZeRO-Offload 1.1 设计原 ...
- P1518 [USACO2.4]两只塔姆沃斯牛 The Tamworth Two
// Problem: P1518 [USACO2.4]两只塔姆沃斯牛 The Tamworth Two // Contest: Luogu // URL: https://www.luogu.com ...
- AOP-基本概念
AOP(概念) 1,什么是AOP (1)面向切面(方面)编程 :利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. (2)通 ...
- Chrome DevTools 面板全攻略
李华西,微医云服务团队前端开发工程师,喜欢瞎折腾,典型猫奴 Console 面板 此章节请打开 devtools/console/console.html 一起食用 一方面用来记录页面在执行过程中的信 ...
- SaltStack 的基本概念与工作原理 架构设计
随着云计算技术的快速普及与发展,越来越多的企业开始学习和搭建自己的云平台代替传统的 IT 交付模式,企业的 IT 环境也随之越来越复杂,常规的运维方法与技术已经无法满足现在云环境中系统的配置与变更.基 ...
- 字节Android Native Crash治理之Memory Corruption工具原理与实践
作者:字节跳动终端技术--庞翔宇 内容摘要 MemCorruption工具是字节跳动AppHealth (Client Infrastructure - AppHealth) 团队开发的一款用于定 ...
- FastDFS文件同步
FastDFS同步相关文件: a)10.100.66.82_23000.mark 内容如下: binlog_index=0 binlog_offset=1334 need_sync_old=1 syn ...
- Vue2技术整理3 - 高级篇 - 更新完毕
3.高级篇 前言 基础篇链接:https://www.cnblogs.com/xiegongzi/p/15782921.html 组件化开发篇链接:https://www.cnblogs.com/xi ...
- scanf用法及scanf中有\n的问题
scanf()函数的原理 想象输入设备(键盘)连接着一个叫"缓冲"的东西,把缓冲认为是一个字符数组. 当你的程序执行到scanf时,会从你的缓冲区读东西,如果缓冲区是空的,就阻塞住 ...
- Java UDP实现聊天功能代码【转】
感谢大佬大佬!!!:https://www.cnblogs.com/woshijpf/p/3735684.html 我以前经常写的是基于TCP的网络编程,由于TCP建立连接鼻血要经过三次握手连接,服务 ...