Подтвердить что ты не робот

Извлечение имен свойств для отражения, с проверкой Intellisense и Compile-Time

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

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

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

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

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

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

или даже

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

будет сладким!

Любые идеи?

4b9b3361

Ответ 1

в 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);

Ответ 2

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

  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);

    }

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

Ответ 3

Рассмотрите возможность использования lambdas или даже 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, если вам нужно разделить абстрактное синтаксическое дерево во время выполнения, сделать отражающие вещи, например получить имя свойства в виде строки; альтернативно, пусть делегат выполнит работу по поиску необходимых вам данных.

Ответ 4

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

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

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";

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

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 сможет показать вам имена свойств из вашего класса в качестве предложений для автоматического завершения.