Использование свойства объекта для ключа в словаре - PullRequest
4 голосов
/ 29 октября 2010

Я хотел бы использовать свойство объектов в качестве ключа для словаря. Можно ли это сделать?

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

Идеальный код, чтобы увидеть, заблокировано ли поле, будет выглядеть так:

bool ageLocked = myObject.IsFieldLocked( x => x.Age);

bool nameLocked = myObject.IsFieldLocked(x => x.Name);

IsFieldLocked - метод расширения для типа myObject.

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

Надеюсь, я смогу использовать фабрику для создания различных вариантов словаря;

Factory.CreateAwaitingOrderLockedFields()

Factory.CreateOrderPlacedLockedFields()

Определение словаря, похожего на это

new Dictionary< ***MissingMagic***, bool>()
{
  { x => x.Age , true},
  { x => x.Name, false}
}

Цель состоит в том, чтобы избежать того, чтобы ключ был строкой, а ключ со строгой типизацией был гораздо более желательным.

Ответы [ 5 ]

4 голосов
/ 29 октября 2010

Я бы определил словарь просто как Dictionary<string, bool>.

Тогда метод расширения мог бы выглядеть примерно так:

public static bool IsFieldLocked<TField>(this MyClass self, Expression<Func<MyClass, TField>> propertyExpression)
{
    // note: null checks etc omitted for brevity

    var lambda = (LambdaExpression)propertyExpression;
    MemberExpression memberExpression;
    if (lambda.Body is UnaryExpression)
    {
        var unaryExpression = (UnaryExpression)lambda.Body;
        memberExpression = (MemberExpression)unaryExpression.Operand;
    }
    else
    {
        memberExpression = (MemberExpression)lambda.Body;
    }

    string propertyName = memberExpression.Member.Name;

    return self.InternalLockedFieldsDictionary[propertyName];
}
1 голос
/ 29 октября 2010

Вот мое сокращенное решение, основанное на советах Герцмайстера дер Вельтена

  public class MyDtoOne : BaseFieldLockingDto<MyDtoOne>
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public MyDtoOne()
        {
            LockedFields = new LockedFields<MyDtoOne>
                               {
                                   { x => x.Age, false }, 
                                   { x => x.Name, true }
                               };
        }
    }

    public class MyDtoTwo : BaseFieldLockingDto<MyDtoTwo>
    {
        public DateTime DateOfBirth { get; set; }

        public MyDtoTwo()
        {
            LockedFields = new LockedFields<MyDtoTwo>
                               {
                                   {x => x.DateOfBirth, false}
                               };
        }
    }

    public class BaseFieldLockingDto<TBaseObject>
    {
        public LockedFields<TBaseObject> LockedFields { get; set; }

        public bool IsFieldLocked<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            return LockedFields.IsFieldLocked(propertyExpression);
        }
    }

    public class LockedFields<TBaseObject> : Dictionary<string, bool>
    {
        public void Add<TField>(Expression<Func<TBaseObject, TField>> propertyExpression, bool isLocked)
        {
            Add(GenerateKey(propertyExpression), isLocked);
        }

        private static string GenerateKey<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            return GetLambdaPropertyName(propertyExpression);
        }

        public bool IsFieldLocked<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            if (Count == 0)
                return false;

            string propertyName = GetLambdaPropertyName(propertyExpression);

            if (ContainsKey(propertyName) == false)
                return false;

            return this[propertyName];
        }

        private static string GetLambdaPropertyName<TField>(Expression<Func<TBaseObject, TField>> propertyExpression)
        {
            var lambda = (LambdaExpression) propertyExpression;
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression) lambda.Body;
                memberExpression = (MemberExpression) unaryExpression.Operand;
            }
            else
            {
                memberExpression = lambda.Body as MemberExpression;
            }

            if (memberExpression == null)
            {
                throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.",
                                                          propertyExpression));
            }

            return memberExpression.Member.Name;
        }
    }

С этим я могу сделать следующее;

             private static void Main(string[] args)
    {
        var myDtoOne = new MyDtoOne();

        bool ageLocked = myDtoOne.IsFieldLocked(x => x.Age);
        bool nameLocked = myDtoOne.IsFieldLocked(x => x.Name);


        Console.WriteLine(string.Format("Age locked is {0}", ageLocked ? "true" : "false"));
        Console.WriteLine(string.Format("Name locked is {0}", nameLocked ? "true" : "false"));

        myDtoOne.LockedFields = new LockedFields<MyDtoOne> {{x => x.Age, true}, {x => x.Name, false}};


        bool ageLocked1 = myDtoOne.IsFieldLocked(x => x.Age);
        bool nameLocked1 = myDtoOne.IsFieldLocked(x => x.Name);

        Console.WriteLine(string.Format("Age locked is {0}", ageLocked1 ? "true" : "false"));
        Console.WriteLine(string.Format("Name locked is {0}", nameLocked1 ? "true" : "false"));


        var myDtoTwo = new MyDtoTwo();

        bool dateOfBirth = myDtoTwo.IsFieldLocked(x => x.DateOfBirth);

        Console.WriteLine(string.Format("Date of birth locked is {0}", dateOfBirth ? "true" : "false"));

        myDtoTwo.LockedFields = new LockedFields<MyDtoTwo>() {{x => x.DateOfBirth, true}};

        bool dateOfBirth1 = myDtoTwo.IsFieldLocked(x => x.DateOfBirth);

        Console.WriteLine(string.Format("Date of birth locked is {0}", dateOfBirth1 ? "true" : "false"));

        Console.ReadLine();
    }
}
0 голосов
/ 29 октября 2010

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

public class MyObject : IComparable {
  public int CompareTo(MyObject obj) {
    // Comparison Logic
  }
}

Так как это на самом деле не вариант для члена объекта, вы, возможно, хорошоиспользуйте Dictionary<string, bool> с именем поля в качестве ключа и небольшим отражением в вашем IsFieldLocked() методе, чтобы убрать строку из строго типизированного поля.

0 голосов
/ 29 октября 2010

Вы можете объявить словарь с любым типом в качестве ключа;

Dictionary<Form,bool>

создаст словарь, в котором элементы формы используются в качестве ключа.

Это то, что вы спрашиваете?

Если в качестве ключа нужно использовать несколько различных объектов, вы можете использовать Dictionary<object,bool> или позволить всем объектам наследоваться от другого объекта Dictionary<masterobject,bool>.

0 голосов
/ 29 октября 2010

Я думаю, вы должны просто использовать наследование. Создайте базовый класс LockedField, затем создайте AwaitingOrderLockedField и OrderPlacedLockedField, которые наследуют этот класс.

class LockedField {
}

class AwaitingOrderLockedField : LockedField {
}

class OrderPlacedLockedField : LockedField {
}

Ваш словарь будет IDictionary<LockedField, bool>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...