ReflectionHelper

Podczas pracy z ComboBox (WinForms) lub DropDownList (ASP.NET) zdarza mi się korzystać z tzw. bindowania. Mam proste DTO Employee i metodę wypełniającą controlkę.

    public class Employee
    {
        public int Key { get; set; }
        public string Name { get; set; }
    }

    private void InitEmployees()
    {
        var employees = new List<Employee>
            {
                new Employee {Key = 1, Name = "Ania"},
                new Employee {Key = 2, Name = "Hania"}
            };

        cmbEmployees.DataSource = employees;
        cmbEmployees.ValueMember = "Key"; // tego typu stringi nigdy mi się nie podobały
        cmbEmployees.DisplayMember = "Name";
    }

Co gdy zmienimy nazwę, jakiejkolwiek właściwości Employee? Kod już nie będzie robił tego co programista miał na myśli („Key” oraz „Name” pozostaną niezmienione).

Refleksja na ratunek

Rozwiązaniem jest użycie refleksji.

    private void InitEmployees()
    {
        var employees = new List<Employee>
            {
                new Employee {Key = 1, Name = "Ania"},
                new Employee {Key = 2, Name = "Hania"}
            };

        cmbEmployees.DataSource = employees;
        // gdy to po raz pierwszy zobaczyłem ogarnęło mnie takie przyjemne uczucie dobrze napisanego kodu:
        cmbEmployees.ValueMember = ReflectionHelper.GetPropertyName<Employee>(t => t.Key);
        cmbEmployees.DisplayMember = ReflectionHelper.GetPropertyName<Employee>(t => t.Name);
    }

Klasa ReflectionHelper (nie mojego autorstwa), którą wklejam w całości, chociaż nie korzystam z pozostałych metod (a nuż się komuś przydadzą):

using System;
using System.Linq.Expressions;
using System.Reflection;

public static class ReflectionHelper
{
    private static PropertyInfo GetPropertyInfoInternal(LambdaExpression propertyAccessor)
    {
        try
        {
            MemberExpression memberExpression;

            if (propertyAccessor.Body is UnaryExpression)
            {
                UnaryExpression ue = (UnaryExpression)propertyAccessor.Body;
                memberExpression = (MemberExpression)ue.Operand;
            }
            else
            {
                memberExpression = (MemberExpression)propertyAccessor.Body;
            }

            return (PropertyInfo)(memberExpression).Member;
        }
        catch (InvalidCastException e)
        {
            throw new ArgumentException(
                "Cannot extract property from expression. Only expression accessing property (like item=>item.Value) are allowed.", e);
        }
    }

    public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, object>> property)
    {
        return GetPropertyInfoInternal(property);
    }

    public static PropertyInfo GetPropertyInfo<T>(T @object, Expression<Func<T, object>> property)
    {
        return GetPropertyInfoInternal(property);
    }


    public static string GetPropertyName<T>(Expression<Func<T, object>> property)
    {
        return GetPropertyInfoInternal(property).Name;
    }

    public static string GetPropertyName<T>(T @object, Expression<Func<T, object>> property)
    {
        return GetPropertyInfoInternal(property).Name;
    }

    public static T GetCustomAttribute<T>(Type t, bool inherit) where T : Attribute
    {
        return (T)Attribute.GetCustomAttribute(t, typeof(T), inherit);
    }
}

Czy ja naprawdę tego potrzebuję?

Uważasz, że takie zmiany nazw się nie zdażają się w twoim projekcie? Mi się zdarzają. Uważam, że są konieczne, bo z czasem programista zauważa, że nazwał źle zmienną za pierwszym razem, że za pierwszym razem nie zrozumiał całej otoczki domenowej, zmieniło się coś w organizacji kodu, itp, itd.

Dla mnie kod żyje cały czas, nie należy się bać refactoringu. Trzeba tylko pisać kod w taki sposób, żeby się przed takimi problemami, jak dzisiejszy, zabezpieczyć.

PS

Pierwszy podwójny post tego samego dnia. Kiedyś myślałem, że lepiej to rozdzielać na dni. Z wielu różnych względów. Jeśli jednak jest wewnętrzna chęć napisania czegoś, to nie należy się ograniczać.

Reklamy
Ten wpis został opublikowany w kategorii Programowanie i oznaczony tagami , , , , , , . Dodaj zakładkę do bezpośredniego odnośnika.

Jedna odpowiedź na „ReflectionHelper

  1. Pingback: dotnetomaniak.pl

Możliwość komentowania jest wyłączona.