博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
当 dynamic 遇上 internal
阅读量:6250 次
发布时间:2019-06-22

本文共 3347 字,大约阅读时间需要 11 分钟。

dynamic 简介

.Net 4 引入了 dynamic,我们可以简单动态访问对象的属性或调用其方法,免去反射的繁琐和不雅:

123
dynamic person = new Person { ID = 1, Name = "鹤冲天" };var name = person.Name;person.Work();

dynamic 对成员的访问是有限制的,只允许访问公有成员,访问私有或保护成员将会抛出异常:

这与于 .net 其它部分是一致的。

dynamic 不支持其它程序集中的 internal 类型

但 dynamic 对其它程序集中的 internal 类确不支持,不能不说是一个遗憾。

试看如下解决方案,其中有两个项目:

ClassLibrary1 是个类库,有两个类:

12345
internal class Student{    public int ID { get; set; }    public string Name { get; set; }}
1234567891011
public class StudentRepository{    public dynamic GetByID(int id)    {        return new Student { ID = id, Name = "鹤冲天" };    }    public dynamic Select()    {        return new { ID = 2, Name = "鹤中天" };    }}

Student 为 internal 类型。

WhenDynamicMeetInternal 项目引用了 ClassLibrary1,在 Program 中编码如下:

123
var repository = new StudentRepository();dynamic student = repository.GetByID(1);var name = student.Name;

调试时,抛出以下异常:

想必大家都想到了,为什么不把 Student 设成 public 呢?

是的,Students 设成 public 后问题轻松解决:

但只解决了部分,却没解决根本,我们来看第二个调用:

12
dynamic student2 = repository.Select();var name2 = student2.Name;

调试时依然有类似异常:

为什么呢?

从上图的 Watch 窗口中我们可以看出匿名类型 student2 的类型不是 public,则其只能是 internal 的(没有 private 的类或结构)。其实匿名类型都是 internal 的,不确定可以自己试下。

我们可以将 Student 设为 public,但对匿名类型,我们却无能为力。

dynamic 拒绝为外部程序集的 internal 类型服务,会带来好多麻烦。下面我们探讨解决办法。

解决办法

使用 InternalsVisibleTo

在 ClassLibrary1 项目的 AssemblyInfo.cs 文件(见上图)末尾中加入一行代码:

1
[assembly: InternalsVisibleTo("WhenDynamicMeetInternal")]

这种方式缺点多多,不推荐,也就不多解释了。

创建新的动态类型 ReflectionDynamicObject

创建类 ReflectionDynamicObject,继承至 DynamicObject,重写 TryGetMember、TryInvokeMember 等几个方法,代码如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445
using System.Dynamic;using System.Globalization;using System.Reflection;internal sealed class ReflectionDynamicObject : DynamicObject{    private object RealObject { get; set; }    public override bool TryConvert(ConvertBinder binder, out object result)    {        result = this.RealObject;        return true;    }    public override bool TryGetMember(GetMemberBinder binder, out object result)    {        PropertyInfo property = this.RealObject.GetType().GetProperty(binder.Name, BindingFlags.GetProperty |             BindingFlags.Public | BindingFlags.Instance);        if (property == null)        {            result = null;        }        else        {            result = property.GetValue(this.RealObject, null);            result = WrapObjectIfInternal(result);        }        return true;    }    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)    {        result = this.RealObject.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod | BindingFlags.NonPublic |             BindingFlags.Public | BindingFlags.Instance, null, this.RealObject, args, CultureInfo.InvariantCulture);        return true;    }    public static object WrapObjectIfInternal(object o)    {        if (o == null) return null;        if (o.GetType().IsPublic) return o;        return new ReflectionDynamicObject { RealObject = o };    }    public override string ToString()    {        return this.RealObject.ToString();    }}

有这个类,就可以解决 internal 的问题了:

12
dynamic student3 = ReflectionDynamicObject.WrapObjectIfInternal(repository.Select());var name3 = student3.Name;

运行截图:

关于 ReflectionDynamicObject

ReflectionDynamicObject 是从 System.Web.WebPages.dll 程序集中反编译出来的:

附源码: (48KB)

转载地址:http://bmusa.baihongyu.com/

你可能感兴趣的文章
自定义异步加载资源插件
查看>>
Mongodb windows 安装
查看>>
easyui combobox两种不同的数据加载方式
查看>>
报错:该页必须具有 <%@ webservice class="MyNamespace.MyClass" ... %> 指令。
查看>>
Smarty配置与实例化
查看>>
***Redis hash是一个string类型的field和value的映射表.它的添加、删除操作都是O(1)(平均)。hash特别适合用于存储对象...
查看>>
抽象方法和接口区别
查看>>
Siege——多线程编程最佳实例
查看>>
c# 生成 验证码
查看>>
Selenium学习(4) 键盘操作
查看>>
SQL Server 触发器
查看>>
神奇语言 python 初识面向对象
查看>>
何为SLAM
查看>>
Effective C++ 条款五 了解C++默默编写并调用哪些函数
查看>>
图的存储结构(邻接矩阵)
查看>>
[工具]infolite-chrome插件css插件
查看>>
javascript 深拷贝
查看>>
SwitchHosts—hosts管理利器
查看>>
【代码小记】无
查看>>
【知识点】Java机密
查看>>