在字典中使用对象属性作为键

4
我希望将对象属性用作字典的键。这可行吗?
最终目标是使用此功能,以便在对象可能处于的各种状态下查看属性是否已锁定。这些锁定值不会被持久化,只存在于模型的业务规则中。
用于检查字段是否已锁定的理想代码如下:
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

我会将字典简单地定义为 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];
}

太棒了,内部存储类型字符串被封装并获得强类型检查,在字典构建和字段锁定检查方面也进行了优化。非常感谢。 - c00ke
@c00ke,不幸的是,在创建表达式树时确实存在一些开销。但是你有多频繁地调用那个方法呢?你需要测试它是否真的对你的情况产生影响。 - herzmeister
它将被我的视图模型在MVC应用程序中使用。将使用IsLockedField函数来从Spark视图启用/禁用UI元素。因此,我认为这取决于视图模型的大小。 - c00ke

1

这是我根据herzmeister der welten的建议简化后的解决方案

  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

我认为你应该使用继承。创建一个基类LockedField,然后创建AwaitingOrderLockedField和OrderPlacedLockedField,它们都继承自这个类。

class LockedField {
}

class AwaitingOrderLockedField : LockedField {
}

class OrderPlacedLockedField : LockedField {
}

你的字典将会是 IDictionary<LockedField, bool>


0

你可以使用任何类型作为字典的键进行声明;

Dictionary<Form,bool>

将创建一个字典,其中表单元素用作键。

这是你所问的吗?

如果要使用多个不同的对象作为键,则可以使用Dictionary<object,bool>或让所有对象继承自另一个对象Dictionary<masterobject,bool>


0

你需要在想要用作字典键的类中实现IComparable接口:

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

由于这对于对象的成员来说并不是一个真正的选项,因此您可以使用Dictionary<string, bool>,将字段名称用作键,并在IsFieldLocked()方法中使用一些反射来从强类型字段中剥离字符串。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接