程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 反射的另一種功能

反射的另一種功能

編輯:關於C#

說到反射嘛,估計各位不陌生,盡管很多人不知道到底是什麼,當然也有人將其看作是“反編譯” ,有點像吧,但不能說是反編譯,雖然有個“反”字,但沒有“編譯”。

所以,我給反射下了 這樣一個概述,准確與否,也不清楚:

反射可以動態獲取程序集信息,或程序集中的類型信息 ,以方便動態調用。

動態調用的好處是節約資源,通常情況下,我們添加程序集引用會在項目 的引用中加入,這意味著只要應用程序運行了,這些程序集就會被加載,不管你是否需要調用。對於調 用較少的程序集,如果考慮在調用時才加載,調用完了就釋放,這樣會在一定程度上節約內存占用,當 然這不是優化代碼的唯一方法,其實代碼優化是沒有固定規則的,綜合而靈活去運用各種技巧都可以優 化代碼和提高性能。

反射重點在於“動態”二字上,我忽然想到了數據綁定,於是我想到一些 東西,平時我們做UI與數據的綁定時,多數情況下是已知數據項目類型有哪些公共屬性,進而綁定到對 象的某個公共屬性上。那麼試想一下,假如我們事先不知道來自數據源的對象是什麼結構,也不清楚它 有哪些公共屬性,這樣數據綁定起來是不是會有麻煩呢?

這顯然是有麻煩的,而且,常規的思 路是很難解決的,那麼這時候我們是不是想到了反射呢?不管來自數據源的是什麼樣的對象類型,我只 要通過反射將它的所有公共屬性都找出來,然後再取出對應屬性的值,再動態生成UI界面。若能如此, 是不是很爽呢?

我這個人有個缺點,就是一旦想到什麼鬼點子,就會迫不及待地試試,故二話 不說,馬上扔掉手頭上的東西,打開電腦,啟動VS,就試著Coding起來了,這一Code還真沒白費力氣, 總算Code出我的預期效果。

以下是我的大致思路:

首先,編寫一個類,提供操作,可以 將任意類型對象的列表轉換成WPF中的UI元素,接著再把轉換出來的UI元素列表當作數據源,賦給 ListBox的ItemsSource屬性,完成綁定。

這樣做的好處就是,不管來自數據源的對象列表中的 類有幾個公共屬性,只要能把它反射出來就行了。

下面是該轉換類的代碼。

using 

System;  
using System.Collections;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using System.Reflection;  
using System.Windows;  
using System.Windows.Controls;  
using System.Windows.Media;  
using System.ComponentModel.DataAnnotations;  
      
namespace MyApp  
{  
    public class ObjectsConvertToUIFrmws  
    {  
        public IList<FrameworkElement> BuildUIList(IList objs)  
        {  
            List<FrameworkElement> uiList = new List<FrameworkElement>();  
            foreach (var obj in objs)  
            {  
                FrameworkElement fe = BuildUICore(obj);  
                uiList.Add(fe);  
            }  
            return uiList;  
        }  
      
      
        private PropertyInfo[] GetPropertiesFromObj(object o)  
        {  
            return o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 

 
        }  
      
        private Panel BuildUICore(object obj)  
        {  
            if (obj == null) return null;  
            PropertyInfo[] pis = GetPropertiesFromObj(obj);  
      
            Grid gridRoot = new Grid();  
            // 有多少個屬性就加入多少行,每行顯示一個  
            gridRoot.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });  
            gridRoot.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });  
            for (int i = 0; i < pis.Length; i++)  
            {  
                gridRoot.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });  
            }  
            for (int n = 0; n < pis.Length;n++ )  
            {  
                var attrs = pis[n].GetCustomAttributes(typeof(DisplayAttribute)).ToArray();  
                // 獲取屬性的值  
                object vl = pis[n].GetValue(obj);  
                if (vl is Color)  
                {  
                    // 如果字段表示顏色,則設置為容器的背景色  
                    Color bgColor = (Color)vl;  
                    gridRoot.Background = new SolidColorBrush(bgColor);  
                }  
                else
                {  
                    TextBlock tbDisplayName = new TextBlock();  
                    if (attrs != null && attrs.Count() > 0)  
                    {  
                        DisplayAttribute dispattr = attrs[0] as DisplayAttribute;  
                        tbDisplayName.Text = dispattr.Name;  
                    }  
                    else
                    {  
                        tbDisplayName.Text = "未知字段";  
                    }  
                    gridRoot.Children.Add(tbDisplayName);  
                    Grid.SetRow(tbDisplayName, n);  
                    Grid.SetColumn(tbDisplayName, 0);  
                    TextBlock tbValue = new TextBlock();  
                    // 如果屬性類型為日期時間,則轉換字符串格式  
                    if (vl is DateTime)  
                    {  
                        tbValue.Text = ((DateTime)vl).ToString("yyyy-MM-dd HH:mm:ss");  
                    }  
                    else if (vl is double)  
                    {  
                        // 如果是雙精度類型,則要保留三位小數  
                        tbValue.Text = ((double)vl).ToString("N3");  
                    }  
                    else
                    {  
                        tbValue.Text = vl.ToString();  
                    }  
                    gridRoot.Children.Add(tbValue);  
                    Grid.SetRow(tbValue, n);  
                    Grid.SetColumn(tbValue, 1);  
                }  
            }  
            return gridRoot;  
        }  
    }  
}

通常情況下,在UI上顯示的字符串最好不是屬性的名字,因為用戶看起來不方便,也可能看 不懂,所以,這裡要求一個規范,就是在定義數據實體類時,為每個屬性加一個DisplayAttribute特性 ,並將其Name屬性設置為要在UI上呈現的字符串。在反射過程中,會取出該特性,並訪問其Name屬性的 值,然後將這個字符串賦給UI象。

比如,現在,我們可以隨便定義一個類來做測試。

public class TestDataItem  
{  
    [Display(Name = "名字:")]  
    public string Name { get; set; }  
    [Display(Name = "年齡:")]  
    public int Age { get; set; }  
    [Display(Name = "城市:")]  
    public string City { get; set; }  
      
    public Color BackGround { get; set; }  
}

由於,在前面的轉換類中,只要碰到是Color結構類型的屬性,就不將其顯示在UI上,而是 作為UI面板容器(Grid對象)的背景色,因此,這裡定義數據對象類的時候,就不用為類型為Color的屬 性添加DisplayAttribute特性了。

你在測試的時候,不一定要TestDataItem類那樣定義,你可 以任意定義實體類,然後通過前面寫的轉換類,將期轉換為UI對象。

public MainWindow()  
{  
    InitializeComponent();  
    List<TestDataItem> list = new List<TestDataItem>();  
    list.Add(new TestDataItem { Name = "abc", Age = 12, City = "大連", BackGround = Colors.Yellow });  
    list.Add(new TestDataItem { Name= "def", Age = 20, City="上海", BackGround = Colors.Pink });  
    list.Add(new TestDataItem { Name = "gao", Age = 60, City="珠海", BackGround = Colors.SkyBlue });  
    ObjectsConvertToUIFrmws cf = new ObjectsConvertToUIFrmws();  
    this.lbList.ItemsSource = cf.BuildUIList(list);  
}

你看,不管定義的實體是什麼結構,都可以動態生成UI元素,如此是不是很方便呢?

上面的例子最後得到如下圖所示的結果。

最後,我聲明一下,我 沒有說非得用這種方法來綁定數據不可,我只是結合了反射的用途而已,不要盲目使用,但我相信肯定 有用,你在使用時不妨考慮一下,哪些情況下可以使用這種思路。

查看本欄目

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