Извлечение имен свойств для отражения с проверкой Intellisense и времени компиляции - PullRequest
14 голосов
/ 28 апреля 2009

Хорошо. Итак, у меня есть некоторый код, который отображает определенные элементы управления в winForm на определенные свойства объекта, чтобы выполнять определенные действия с элементами управления, когда определенные вещи происходят с данными. Все хорошо, хорошо работает. Не проблема. Проблема в том, что для добавления элементов в отображение я вызываю функцию, которая выглядит следующим образом:

this.AddMapping(this.myControl,myObject,"myObjectPropertyName");

Проблема, с которой я сталкиваюсь, заключается в том, что во время компиляции очень трудно определить разницу между приведенной выше строкой и приведенной ниже:

this.AddMapping(this.myControl,myObject,"myObjectPropretyName");

Поскольку последний параметр является строкой, нет никакой проверки времени компиляции или чего-либо подобного, что могло бы обеспечить, что сама строка действительно соответствует действительному имени свойства для данного объекта. Кроме того, такие вещи, как Refactor и «Найти все ссылки», упускают такой тип ссылок, что приводит к веселью при изменении имени самого свойства. Так что мне интересно, есть ли какой-нибудь способ изменить функцию так, чтобы то, что я передаю, все еще представляло собой строку, представляющую имя свойства, но с проверкой фактического значения во время компиляции. Кто-то сказал, что я мог бы сделать это с деревьями выражений, но я прочитал их и, похоже, не вижу связи. Я хотел бы сделать что-то вроде:

this.AddMapping(this.myControl,myObject,myObject.myObjectPropertyName);

или даже

this.AddMapping(this.myControl,myObject.myObjectPropertyName);

было бы сладко!

Есть идеи?

Ответы [ 5 ]

15 голосов
/ 28 апреля 2009

в 3.5, выражение - это один из способов указать имена членов в виде кода; Вы могли бы иметь:

public void AddMapping<TObj,TValue>(Control myControl, TObj myObject,
       Expression<Func<TObj, TValue>> mapping) {...}

и затем проанализируйте дерево выражений, чтобы получить значение. Немного неэффективно, но не так уж плохо.

Вот пример кода:

    public void AddMapping<TSource, TValue>(
        Control control,
        TSource source,
        Expression<Func<TSource, TValue>> mapping)
    {
        if (mapping.Body.NodeType != ExpressionType.MemberAccess)
        {
            throw new InvalidOperationException();
        }
        MemberExpression me = (MemberExpression)mapping.Body;
        if (me.Expression != mapping.Parameters[0])
        {
            throw new InvalidOperationException();
        }
        string name = me.Member.Name;
        // TODO: do something with "control", "source" and "name",
        // maybe also using "me.Member"
    }

вызывается с помощью:

    AddMapping(myControl, foo, f => f.Bar);
3 голосов
/ 26 октября 2012

Чтобы упростить работу с помощью лямда-решения на основе Expression, я написал его как метод расширения.

  public static string GetPropertyName<T>(this object o, Expression<Func<T>> property)
    {
        var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
        if (propertyInfo == null)
            throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
        var propertyName = propertyInfo.Name;
        return propertyName;
    }

Звоните вот так

    class testclass
    {
        public string s { get; set; }
        public string s2 { get; set; }
        public int i { get; set; }

    }

    [TestMethod]
    public void TestMethod2()
    {
        testclass x = new testclass();
        string nameOfPropertyS = this.GetPropertyName(() => x.s);
        Assert.AreEqual("s", nameOfPropertyS);

        string nameOfPropertyI = x.GetPropertyName(() => x.i);
        Assert.AreEqual("i", nameOfPropertyI);

    }

Хорошо, использование в качестве метода расширения действительно для удобства, так как вы можете вызвать метод одного класса для свойств другого класса. Я уверен, что это может быть улучшено.

2 голосов
/ 28 апреля 2009

Рассмотрите возможность использования лямбды или даже System.Linq.Expressions для этого с одним из:

extern void AddMapping<T,U>(Control control, T target, Func<T,U> mapping);
extern void AddMapping<T,U>(Control control, T target, Expression<Func<T,U>> mapping);

Тогда назовите это с

this.AddMapping(this.myControl, myObject, (x => x.PropertyName));

Используйте аргумент Expression, если вам нужно разбирать абстрактное синтаксическое дерево во время выполнения, чтобы делать такие вещи, как получение имени свойства в виде строки; в качестве альтернативы, пусть делегат выполнит работу по отбору нужных вам данных.

1 голос
/ 09 сентября 2009

То, что вы ищете, называется статическим отражением. Бесстыдная заглушка => http://emiajnet.blogspot.com/2009/05/getting-fun-with-net-static-reflection.html И гораздо лучшая статья здесь: http://www.lostechies.com/blogs/gabrielschenker/archive/2009/02/03/dynamic-reflection-versus-static-reflection.aspx

1 голос
/ 28 апреля 2009

Вы действительно не должны передавать строковые литералы в качестве имен свойств. Вместо этого вы должны использовать YourClass.PROPERTY_NAME_FOO.

Вы должны объявить эти строки как константы в вашем классе.

public const String PROPERTY_NAME_FOO = "any string, it really doesn't matter";
public const String PROPERTY_NAME_BAR = "some other name, it really doesn't matter";

Или вам не нужно беспокоиться о строках, просто имена свойств:

public const int PROPERTY_NAME_FOO = 0;
public const int PROPERTY_NAME_BAR = 1; //values don't matter, as long as they are unique

Это остановит строки, которые не ссылаются на допустимое свойство, от вызова функций.

Intelisense сможет показывать вам имена свойств из вашего класса в качестве подсказок для автозаполнения.

...