服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C# 反射与dynamic最佳组合示例代码

C# 反射与dynamic最佳组合示例代码

2022-01-21 13:48DW039 C#

这篇文章主要介绍了C# 反射与dynamic最佳组合示例代码,需要的朋友可以参考下

在 c# 中反射技术应用广泛,至于什么是反射.........你如果不了解的话,请看下段说明,否则请跳过下段。广告一下:喜欢我文章的朋友请关注一下我的blog,这也有助于提高本人写作的动力。

反射:当你背对一个美女或帅哥却不能回头仔细观察研究时(纯属虚构,如有巧合、纯属雷同),一面小镜子就能满足你的需求。在 c# 编程过程中也经常遇到类似的情况:有一个别人写的 dll 类库你想使用却没程序文档资料......此时通过 c# runtime 提供的功能,你可以把该 dll 类库加载到你的程序中,并细细研究 dll 的每一部分内容,这就是 c# 中的反射。

个人认为反射最突出的优点或存在的合理性:在不修改程序原码的情况下,实现程序功能的动态调整(runtime动态对象创建)

示例:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface irun {
 void run();
}
class person : irun
{
 public void run()
 {
  console.writeline("走,去lol啊!");
 }
}
class car : irun
{
 public void run()
 {
  console.writeline("呜...........");
 }
}
class program
{
 static void main(string[] args)
 {
  irun e = new person();
  e.run();
  console.readline();
 }
}

如果将上面的run功能并不一定是由person来执行,有时需要是car有时需要person。常见的解决方案是添加 if 等判断结构,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void main(string[] args)
 {
  console.writeline("请输入:car或person");
  string type = console.readline();
  irun e = null;
  if ("car" == type)
  {
   e = new car();
  }else if("person" == type)
  {
   e = new person();
  }
  if(null != e)
   e.run();
  console.readline();
 }

这种结构确是解决了现在的需求,但并不健壮。随着 irun 接口实现、相关类的继承的增加,上面的判断结构也会飞速增长。面向对象编程、设计模式均遵循的一大原则就是封装变换,所以上面的程序无法很好的应对变化。在此我们并不涉及 “设计模式的” 的知识,因此下面的示例代码只为简化上面的程序、并未刻意套用设计模式相关知识。如下:     

?
1
2
3
4
5
6
7
8
9
10
11
static void main(string[] args)
 {
  console.writeline("请输入:car或person");
  string type = console.readline();
  string classpath = string.format("namespace.{0}", type);
  irun e = activator.createinstance(null, classpath).unwrap() as irun;
 
  if(null != e)
   e.run();
  console.readline();
 }

经过上面的修改,程序可自行根据用户的输入,通过activator.createinstance创建 irun 的实例,程序此处不会再随 irun 的实现者增多这种问题的影响而发生变化。上面的这种优点就是通过反射得到的,也是我所认为的“反射存在的合理性”。

activator、assembly 实现反射方式创建对象

c#中反射方式创建对象可以通过 activator.createinstance(静态)和 assembly.createinstance(非静态)来实现,其中assembly.createinstance 内部调用的仍是activator.createinstance。

根据要动态创建的类型对象是否处于当前程序集之中,可将反射创建对象分为:创建程序集内的类型对象与创建程序集外的类型对象。

创建程序集内的类型对象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void reflectionirun1(string classname)
{
 string classpath = string.format("namespace.{0}", classname);
 //参数 null ,指出所要创建类型对象位于当前程序集
 var handler = activator.createinstance(null, classpath);
 irun e = (irun)handler.unwrap();
 console.writeline(e.run());
}
private static void reflectionirun2(string classname)
{
 string classpath = string.format("namespace.{0}", classname);
 //typeof(irun).assembly 获取 irun 类型所在的程序集
 object obj = typeof(irun).assembly.createinstance(null, classpath);
 irun e = (irun)obj;
 console.writeline(e.run());
}

创建程序集外的类型对象

项目中增加一个 类库 (另一个程序集),如下图:

C# 反射与dynamic最佳组合示例代码

添加一个 boss 类,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
namespace lib
{
 public class boss
 {
  private string name = "老大";
  
  public string name{
   get {return name;}
  }
  public string talk()
  {
   return "你们都被开除了......";
  }
  //老板不会算账,总是多付钱,所以很有自知之明的将payfor设为private,防止外部人员调用
  private int payfor(int total)
  {
   return total + 10;
  }
 }
}

获取 一个 boss 对象前,首先添加对 lib 的引用,获取示例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void reflectionboss1()
 {
  string classpath ="lib.boss";
  //"lib" 参数指明要加载的程序集(即要创建的对象类型在哪个程序集中定义)
  var handler = activator.createinstance("lib", classpath);
  boss b = handler.unwrap() as boss;
  console.writeline(b.talk());
 }
 private static void reflectionboss2()
 {
  string classpath ="lib.boss";
  //assembly.load("lib") 加载的程序集(即要创建的对象类型在哪个程序集中定义)
  var assembly = assembly.load("lib");
  boss b = (boss)assembly.createinstance(classpath);
  console.writeline(b.talk());
 }

关于反射时clr如何查找并定位要加载的程序集,请参考msdn中关于反射相关的知识。

反射访问字段、调用方法(属性)

反射除可以帮我们动态创建对象外,还可帮我们动态访问对象的方法(属性)或字段,因 c# 版本不同具体方法会有变更或扩展,更深入内容请参考msdn。下面仅作简单示例(标准用法)。

给老板改名,示例:      

?
1
2
3
4
5
6
7
8
9
10
11
private static void reflectionboss1()
 {
  string classpath = "lib.boss";
  //"lib" 参数指明要加载的程序集(即要创建的对象类型在哪个程序集中定义)
  var handler = activator.createinstance("lib", classpath);
  boss b = handler.unwrap() as boss;
  //关键代码
  fieldinfo f = b.gettype().getfield("name", bindingflags.getfield | bindingflags.nonpublic | bindingflags.instance);
  f.setvalue(b, "小二");
  console.writeline("{0}:{1}", b.name, b.talk());
 }

输出:

C# 反射与dynamic最佳组合示例代码

让老板付钱:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void reflectionboss1()
  {
   string classpath = "lib.boss";
   //"lib" 参数指明要加载的程序集(即要创建的对象类型在哪个程序集中定义)
   var handler = activator.createinstance("lib", classpath);
   boss b = handler.unwrap() as boss;
   //关键代码
   methodinfo method = b.gettype().getmethod("payfor", bindingflags.invokemethod | bindingflags.nonpublic | bindingflags.instance);
   object money = method.invoke(b, new object[] { 10 });
   console.writeline("dw039:老大给我报销10元钱车费......");
   console.writeline("{0}:.....,算不清了,给你这些吧。",b.name);
   console.writeline("dw039:......");
   console.writeline("{0}:{1}", b.name,money);
   console.writeline("dw039:老大你真棒!");
  }

输出:

C# 反射与dynamic最佳组合示例代码

dynamic 与 反射 双剑合璧

因为反射是运行时的类型操作,所以在编程时面临类型不确定的问题。根据上一篇《c# 匿名对象(匿名类型)、var、动态类型 dynamic》讲得 dynamic 动态类型结合我们编写的反射程序,可以大大优化程序逻辑(访问受保护级别限制的代码不在此范围内)。

上面代码的优化:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void reflectionboss1()
  {
   string classpath ="lib.boss";
   var handler = activator.createinstance("lib", classpath);
   dynamic b = handler.unwrap();
   console.writeline(b.talk());
  }
  private static void reflectionboss2()
  {
   string classpath ="lib.boss";
   var assembly = assembly.load("lib");
   dynamic b = assembly.createinstance(classpath);
   console.writeline(b.talk());
  }

通过 dynamic 动态类型对象 b 来调用反射得到对象的属性、方法可直接调用,从而省去了频繁的类型转换操作。

反射常见应用场景

应用场景我印象最深刻的是 ms petshop 示例,从sql server 数据库切换到 oracle 数据库时反射获得不同的数据访问层。然我实际项目中从未遇到过中途切换数据库的情况,其他应用场景基本类似上面的示例。如果朋友你发现更多的应用场景,请给予补充,3ks。

反射的优缺点

优点:反射使程序更灵活

缺点:反射运行速度相对较慢

至于反射相比普通程序慢,我没有进行过测试也不打算进行。现实情况是:ms提倡使用 dynamic、mvc流行、ms对clr不断优化、机器性能的提升,所以你在开发中无需过多考虑反射的性能问题。如果你写的程序运行速度出现了瓶颈(应首先确保自己程序写的合理),研究一下数据库优化、数据缓存、web缓存、负载均衡等技术我认为更实际一些。

原文链接:http://www.cnblogs.com/dw039/p/7476011.html

延伸 · 阅读

精彩推荐
  • C#C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    C#实现的文件操作封装类完整实例【删除,移动,复制,重命名】

    这篇文章主要介绍了C#实现的文件操作封装类,结合完整实例形式分析了C#封装文件的删除,移动,复制,重命名等操作相关实现技巧,需要的朋友可以参考下...

    Rising_Sun3892021-12-28
  • C#聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题 新手速来围观

    聊一聊C#接口问题,新手速来围观,一个通俗易懂的例子帮助大家更好的理解C#接口问题,感兴趣的小伙伴们可以参考一下...

    zenkey7072021-12-03
  • C#C#直线的最小二乘法线性回归运算实例

    C#直线的最小二乘法线性回归运算实例

    这篇文章主要介绍了C#直线的最小二乘法线性回归运算方法,实例分析了给定一组点,用最小二乘法进行线性回归运算的实现技巧,具有一定参考借鉴价值,需要...

    北风其凉8912021-10-18
  • C#浅谈C# winForm 窗体闪烁的问题

    浅谈C# winForm 窗体闪烁的问题

    下面小编就为大家带来一篇浅谈C# winForm 窗体闪烁的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    C#教程网7962021-12-21
  • C#c#学习之30分钟学会XAML

    c#学习之30分钟学会XAML

    一个界面程序的核心,无疑就是界面和后台代码,而xaml就是微软为构建应用程序界面而创建的一种描述性语言,也就是说,这东西是搞界面的...

    C#教程网8812021-12-10
  • C#Unity3D UGUI实现缩放循环拖动卡牌展示效果

    Unity3D UGUI实现缩放循环拖动卡牌展示效果

    这篇文章主要为大家详细介绍了Unity3D UGUI实现缩放循环拖动展示卡牌效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参...

    诗远3662022-03-11
  • C#C#基础之泛型

    C#基础之泛型

    泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个新功能。接下来通过本文给大家介绍c#基础之泛型,感兴趣的朋友一起学习吧...

    方小白7732021-12-03
  • C#C# 后台处理图片的几种方法

    C# 后台处理图片的几种方法

    本篇文章主要介绍了C# 后台处理图片的几种方法,非常具有实用价值,需要的朋友可以参考下。...

    IT小伙儿10162021-12-08