Как сделать Привязку данных безопасной с точки зрения типов и рефакторинг поддержки

Моей самой большой проблемой являются производители браузера. Высокомерный небольшой *^& %s. Я имею в виду, Вы не можете продать браузер никому, все же все находятся в их небольшой угловой попытке сделать друг друга, только создавая подразделение. О, и Chrome. Chrome все еще не знает то, чем он хочет быть, Safari или Firefox. Кроме его одного светского таланта, его практически бесполезного. Я обвиняю все Вы парни, которые продолжали гуглить просто, потому что Вы ненавидите монополии. Угадайте то, что, они - монополия теперь. Теперь мы можем все наслаждаться второсортным, преждевременно запущенным программным обеспечением.

Это главным образом происходит от ошибки*, я имел в Chrome сегодня, он никогда не рассветал на мне для запросов браузера. И Safari и Chrome перестали работать так, я фигурировал эй, после того как я решил проблему Safari, Chrome будет зафиксирован автоматически, но о нет нет. Г-н "Я работаю, вкладки в отдельных процессах" иначе "полный экран сэра No" просто должны были содержать меня в блокировке ящерицы с ее умом, пугающимся реализация.

я также терпеть не могу Firefox. Я не могу сказать, есть ли у меня заражение вирусами или выполнение Firebug. Теперь, пока Adobe не решает выпустить браузер, который делает Flash практичным для вещей кроме фрагментов ролика, о которых я в значительной степени собираюсь иметь что-то для обмана в течение долгого времени. И все мы знаем, что это - то, о чем жизнь - все.

кроме того, я только люблю программировать, когда я встречаюсь со смешными ошибками, которые делают мои мозговые соки выполненными.

69
задан Community 23 May 2017 в 10:30
поделиться

3 ответа

Благодаря Оливеру за то, что он помог мне начать работу, теперь у меня есть решение, которое оба поддерживают рефакторинг и являются типобезопасными. Он также позволил мне реализовать INotifyPropertyChanged, чтобы он справлялся с переименовываемыми свойствами.

Его использование выглядит так:

checkBoxCanEdit.Bind(c => c.Checked, person, p => p.UserCanEdit);
textBoxName.BindEnabled(person, p => p.UserCanEdit);
checkBoxEmployed.BindEnabled(person, p => p.UserCanEdit);
trackBarAge.BindEnabled(person, p => p.UserCanEdit);

textBoxName.Bind(c => c.Text, person, d => d.Name);
checkBoxEmployed.Bind(c => c.Checked, person, d => d.Employed);
trackBarAge.Bind(c => c.Value, person, d => d.Age);

labelName.BindLabelText(person, p => p.Name);
labelEmployed.BindLabelText(person, p => p.Employed);
labelAge.BindLabelText(person, p => p.Age);

Класс person показывает, как реализовать INotifyPropertyChanged безопасным способом (или см. Этот ответ для другой довольно хороший способ реализации INotifyPropertyChanged, ActiveSharp - автоматический INotifyPropertyChanged тоже выглядит неплохо):

public class Person : INotifyPropertyChanged
{
   private bool _employed;
   public bool Employed
   {
      get { return _employed; }
      set
      {
         _employed = value;
         OnPropertyChanged(() => c.Employed);
      }
   }

   // etc

   private void OnPropertyChanged(Expression<Func<object>> property)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, 
             new PropertyChangedEventArgs(BindingHelper.Name(property)));
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;
}

Вспомогательный класс привязки WinForms содержит основу, которая заставляет все это работать:

namespace TypeSafeBinding
{
    public static class BindingHelper
    {
        private static string GetMemberName(Expression expression)
        {
            // The nameof operator was implemented in C# 6.0 with .NET 4.6
            // and VS2015 in July 2015. 
            // The following is still valid for C# < 6.0

            switch (expression.NodeType)
            {
                case ExpressionType.MemberAccess:
                    var memberExpression = (MemberExpression) expression;
                    var supername = GetMemberName(memberExpression.Expression);
                    if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;
                    return String.Concat(supername, '.', memberExpression.Member.Name);
                case ExpressionType.Call:
                    var callExpression = (MethodCallExpression) expression;
                    return callExpression.Method.Name;
                case ExpressionType.Convert:
                    var unaryExpression = (UnaryExpression) expression;
                    return GetMemberName(unaryExpression.Operand);
                case ExpressionType.Parameter:
                case ExpressionType.Constant: //Change
                    return String.Empty;
                default:
                    throw new ArgumentException("The expression is not a member access or method call expression");
            }
        }

        public static string Name<T, T2>(Expression<Func<T, T2>> expression)
        {
            return GetMemberName(expression.Body);
        }

        //NEW
        public static string Name<T>(Expression<Func<T>> expression)
        {
           return GetMemberName(expression.Body);
        }

        public static void Bind<TC, TD, TP>(this TC control, Expression<Func<TC, TP>> controlProperty, TD dataSource, Expression<Func<TD, TP>> dataMember) where TC : Control
        {
            control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));
        }

        public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)
        {
            // as this is way one any type of property is ok
            control.DataBindings.Add("Text", dataObject, Name(dataMember));
        }

        public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)
        {       
           control.Bind(c => c.Enabled, dataObject, dataMember);
        }
    }
}

В нем используется много нового в C # 3.5 и показывает, что возможно. Если бы только у нас были гигиенические макросы программист на Lisp мог бы перестать называть нас гражданами второго сорта)

51
ответ дан 24 November 2019 в 13:51
поделиться

Оператор nameof был реализован в C # 6.0 с .NET 4.6 и VS2015 в июле 2015 года. Следующее по-прежнему верно для C # <6.0

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

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

public static class Member
{
    private static string GetMemberName(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression) expression;
                var supername = GetMemberName(memberExpression.Expression);

                if (String.IsNullOrEmpty(supername))
                    return memberExpression.Member.Name;

                return String.Concat(supername, '.', memberExpression.Member.Name);

            case ExpressionType.Call:
                var callExpression = (MethodCallExpression) expression;
                return callExpression.Method.Name;

            case ExpressionType.Convert:
                var unaryExpression = (UnaryExpression) expression;
                return GetMemberName(unaryExpression.Operand);

            case ExpressionType.Parameter:
                return String.Empty;

            default:
                throw new ArgumentException("The expression is not a member access or method call expression");
        }
    }

    public static string Name<T>(Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

    public static string Name<T>(Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }
}

Вы можете использовать этот класс следующим образом. Хотя вы можете использовать его только в коде (а не в XAML), он весьма полезен (по крайней мере, для меня), но ваш код по-прежнему небезопасен. Вы можете расширить имя метода с помощью аргумента второго типа, который определяет возвращаемое значение функции, которое ограничивает тип свойства.

var name = Member.Name<MyClass>(x => x.MyProperty); // name == "MyProperty"

До сих пор я не нашел ничего, что решало бы проблему безопасности типов привязки данных.

С уважением

27
ответ дан 24 November 2019 в 13:51
поделиться

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

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

Примерно так:

public class DummyViewModel : ViewModelBase
{
    private class DummyViewModelPropertyInfo
    {
        internal readonly string Dummy;

        internal DummyViewModelPropertyInfo(DummyViewModel model)
        {
            Dummy = BindingHelper.Name(() => model.Dummy);
        }
    }

    private static DummyViewModelPropertyInfo _propertyInfo;
    private DummyViewModelPropertyInfo PropertyInfo
    {
        get { return _propertyInfo ?? (_propertyInfo = new DummyViewModelPropertyInfo(this)); }
    }

    private string _dummyProperty;
    public string Dummy
    {
        get
        {
            return this._dummyProperty;
        }
        set
        {
            this._dummyProperty = value;
            OnPropertyChanged(PropertyInfo.Dummy);
        }
    }
}
5
ответ дан 24 November 2019 в 13:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: