其实,反射还有另一种功能

说到反射嘛,估计各位不陌生,尽管很多人不知道到底是什么,当然也有人将其看作是“反编译”,有点像吧,但不能说是反编译,虽然有个“反”字,但没有“编译”。

所以,我给反射下了这样一个概述,准确与否,也不清楚:

反射可以动态获取程序集信息,或程序集中的类型信息,以方便动态调用

动态调用的好处是节约资源,通常情况下,我们添加程序集引用会在项目的引用中加入,这意味着只要应用程序运行了,这些程序集就会被加载,不管你是否需要调用。对于调用较少的程序集,如果考虑在调用时才加载,调用完了就释放,这样会在一定程度上节约内存占用,当然这不是优化代码的唯一方法,其实代码优化是没有固定规则的,综合而灵活去运用各种技巧都可以优化代码和提高性能。

 

反射重点在于“动态”二字上,我忽然想到了数据绑定,于是我想到一些东西,平时我们做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元素,如此是不是很方便呢?

上面的例子最后得到如下图所示的结果。

最后,我声明一下,我没有说非得用这种方法来绑定数据不可,我只是结合了反射的用途而已,不要盲目使用,但我相信肯定有用,你在使用时不妨考虑一下,哪些情况下可以使用这种思路。

好吧,不写太长的F话了,到此为止吧,拜拜。

猜你喜欢

转载自blog.csdn.net/tcjiaan/article/details/9024061
今日推荐