程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 當dynamic遇上internal

當dynamic遇上internal

編輯:C#入門知識

dynamic 簡介
.Net 4 引入了 dynamic,我們可以簡單動態訪問對象的屬性或調用其方法,免去反射的繁瑣和不雅:

1 dynamic person = new Person { ID = 1, Name = "鶴沖天" };
2 var name = person.Name;
3 person.Work();

dynamic 對成員的訪問是有限制的,只允許訪問公有成員,訪問私有或保護成員將會拋出異常:

image

這與於 .net 其它部分是一致的。

dynamic 不支持其它程序集中的 internal 類型
但 dynamic 對其它程序集中的 internal 類確不支持,不能不說是一個遺憾。

試看如下解決方案,其中有兩個項目:

image

ClassLibrary1 是個類庫,有兩個類:

1
2
3
4
5 internal class Student
{
    public int ID { get; set; }
    public string Name { get; set; }
}

1
2
3
4
5
6
7
8
9
10
11 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 中編碼如下:

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

調試時,拋出以下異常:

image

想必大家都想到了,為什麼不把 Student 設成 public 呢?

是的,Students 設成 public 後問題輕松解決:

image

但只解決了部分,卻沒解決根本,我們來看第二個調用:

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

調試時依然有類似異常:

image

為什麼呢?

從上圖的 Watch 窗口中我們可以看出匿名類型 student2 的類型不是 public,則其只能是 internal 的(沒有 private 的類或結構)。其實匿名類型都是 internal 的,不確定可以自己試下。

我們可以將 Student 設為 public,但對匿名類型,我們卻無能為力。

dynamic 拒絕為外部程序集的 internal 類型服務,會帶來好多麻煩。下面我們探討解決辦法。

解決辦法
使用 InternalsVisibleTo

image
在 ClassLibrary1 項目的 AssemblyInfo.cs 文件(見上圖)末尾中加入一行代碼:

1 [assembly: InternalsVisibleTo("WhenDynamicMeetInternal")]

這種方式缺點多多,不推薦,也就不多解釋了。

創建新的動態類型 ReflectionDynamicObject
創建類 ReflectionDynamicObject,繼承至 DynamicObject,重寫 TryGetMember、TryInvokeMember 等幾個方法,代碼如下:

1 using System.Dynamic;
2using System.Globalization;
3using System.Reflection;

4

5internal sealed class ReflectionDynamicObject : DynamicObject
6{
7private object RealObject { get; set; }

8

9    public override bool TryConvert(ConvertBinder binder, out object result)
10   {
11      result = this.RealObject;

12      return true;
13   }
14   public override bool TryGetMember(GetMemberBinder binder, out object result)
15    {
16        PropertyInfo property = this.RealObject.GetType().GetProperty(binder.Name, BindingFlags.GetProperty |
17           BindingFlags.Public | BindingFlags.Instance);
18        if (property == null)
19        {
20            result = null;
21        }
22       else
23        {
24            result = property.GetValue(this.RealObject, null);
25            result = WrapObjectIfInternal(result);
26        }

27       return true;
28    }
29    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
30   {
31       result = this.RealObject.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod |                 

32 BindingFlags.NonPublic |
33            BindingFlags.Public | BindingFlags.Instance, null, this.RealObject, args, CultureInfo.InvariantCulture);
34        return true;
35    }
36    public static object WrapObjectIfInternal(object o)
37    {
38        if (o == null) return null;

39       if (o.GetType().IsPublic) return o;
40       return new ReflectionDynamicObject { RealObject = o };
41   }
42    public override string ToString()
43    {

44       return this.RealObject.ToString();
45    }
46}

有這個類,就可以解決 internal 的問題了:

1 dynamic student3 = ReflectionDynamicObject.WrapObjectIfInternal(repository.Select());
2 v

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved