2009-06-15 31 views
1

我有一个自定义类(调用我的字段),它实现了几个属性。其中一个属性是MaximumLength,它表示该值的最大长度。 Value属性是一个对象,所以我可以设置为字符串,int,double等等。然后我有一个类有多个Field类型的属性。所有的Field属性都在构造函数中初始化,只有Field.Value属性可以写入。如果试图将Field.Value设置为对于字段来说太长的值并实现INotifyPropertyChanged,我想要做的就是抛出一个错误。我的问题是Value属性是泛型Field类的成员,我不知道如何获取该类内的属性名称。如何获取myClass类型的属性的名称?

一个例子:

public class Customer 
{ 
    private Field _firstName = new Field(typeof(string), 20); 

    public Field FirstName 
    { 
     get 
     { 
     return _firstName; 
     } 
    } 
} 

public class Field 
{ 
    private Type _type; 
    private int _maximumLength; 
    object _value; 

    public Field(Type type, int maximumLength) 
    { 
     _type = type; 
     _maximumLength = maximumLength; 
    }   

    public Object Value 
    { 
     get 
     { 
     return _value; 
     } 
     set 
     { 
     if (value.ToString().Length > _maximumLength) 
     { 
      throw(string.Format("{0} cannot exceed {1} in length.", property name, _maximumValue); 
     } 
     else 
     { 
      _value = value; 
      OnPropertyChanged(property name); 
     } 
     } 
    } 
} 

希望这是清楚。

+0

最接近你会得到这是实际属性的上下文访问,该领域本身不会知道它的起源,毕竟,它只是一个实例..上下文的事情 - 即..即时通讯访问此属性以得到这个字段,然后打开反射将是非常严格的 - 像WPF的依赖系统类似的系统证明,在这种情况下,没有比简单地以字符串形式复制名称更好的方法..或者你可以看看面向方面的系统,比如postsharp,它可以让你编译时间重写IL。 – meandmycode 2009-06-16 14:31:12

回答

0

在我看来,是的,可能有一种使用反射来获取这些信息的方法,但我不确定这是否是一种万无一失的方法,并且可能会让您在稍后进行更麻烦的调试阶段上。它似乎过分...聪明。

在我看来,根据我过去的实施情况,Fredrik和Micahtan正指出你的方向是正确的。你的Field类应该实现一个Name属性,在实例化时设置。我可以指出这是一个好主意的一个原因是,这是微软做这件事的方式。如果您查看任何可视设计器的生成代码,控件将实现由设计者设置的Name属性。如果在封面下面有一个可靠的方法来做到这一点,那么你就不得不相信这会完成。

使用Name属性的另一个好处是,它允许您为您的属性提供“英文”翻译。即“First Name”而不是“FirstName”,这是对用户界面友好的方法,并将用户从你的实现中解耦出来(以使“解耦”这个词超载)。

0

为什么不简单地扩展Field类来保存名称,并在构造函数中传递它呢?

private Field _firstName = new Field(typeof(string), "FirstName", 20); 

Field类是这样的:

public class Field 
{ 
    private Type _type; 
    private int _maximumLength; 
    private string _name; 
    object _value; 

    public Field(Type type, string name, int maximumLength) 
    { 
     _type = type; 
     _maximumLength = maximumLength; 
     _name = name; 
    }   

    public Object Value 
    { 
     get 
     { 
     return _value; 
     } 
     set 
     { 
     if (value.ToString().Length > _maximumLength) 
     { 
      throw new SomeException(string.Format("{0} cannot exceed {1} in length.", _name, _maximumValue)); 
     } 
     else 
     { 
      _value = value; 
      OnPropertyChanged(_name); 
     } 
     } 
    } 
} 
+0

我曾考虑过这个问题,但对于我来说似乎是多余的,因为该属性有一个名称,如果我或其他人开始重命名属性,则会增加一个步骤。如果我不能得到另一种方法,我可能会回避。 – jac 2009-06-15 20:12:04

0

同意瓦特/弗雷德里克。

但是,如果您希望在Field数据中具有某种类型安全性,也应该考虑使用泛型,即将Field类重新定义为字段,其中存储T _value而不是对象。

我还以为你必须有不同的算法来长检查不同类型(如字符串与数字等),以及空值检查等

HTH。

0

我同意以上所述。该字段应具有自己的名称属性,该名称属性是用构造设置的。

我不同意用泛型实现这个。类型安全是一个很好的目标,但您可能还需要能够存储不同类型的Field对象的集合/列表

如果没有通用类也实现通用接口,泛型不会让你这样做。

您可以实现一个共同的接口,但那么你就需要值为类型的对象,不是类型<牛逼>,让你失去了仿制药的性能优势。

+0

@yshuditelu - 我误解了问题,更新了答案。 – richardtallent 2009-06-15 20:26:44

+0

我同意,如果需要一个Field的集合,那么可能泛型不是要走的路,或者确实需要添加接口来使通用实现工作。但是,如果Field仅仅用于封装某些验证逻辑,那么我相信泛型可能是合适的并且是首选。 – 2009-06-15 20:47:07

0

我相信这就是你想要发生的事情。我将泛型添加到类中,而不是使用对象来添加类型安全性。我还添加了一个明确的异常类型,以便编译,但Exception类型可以是任何需要字符串的类型。

public class Customer 
{ 
    private Field<string> _firstName = new Field<string>("FirstName", 20); 

    public Field<string> FirstName 
    { 
     get 
     { 
      return _firstName; 
     } 
    } 
} 

public class Field<T> 
{ 
    private int _maximumLength; 
    private T _value; 
    private string _propertyName; 

    public Field(string propertyName, int maximumLength) 
    { 
     _propertyName = propertyName; 
     _maximumLength = maximumLength; 
    } 

    public T Value 
    { 
     get 
     { 
      return _value; 
     } 
     set 
     { 
      if (value.ToString().Length > _maximumLength) 
      { 
       throw new ArgumentException(string.Format("{0} cannot exceed {1} in length.", _propertyName, _maximumLength)); 
      } 
      else 
      { 
       _value = value; 
       OnPropertyChanged(_propertyName); 
      } 
     } 
    } 
} 

编辑:

响应您的评论:“我想过这个问题,但它似乎是多余的,因为我的属性有一个名称,并增加了一个步骤,如果我或其他人开始重命名属性我可能会回避,但如果我不能得到另一种方法。“

我相信你所要求的是某种反射代码,它会在从Field通知属性更改时检索Customer中的属性名称。我不相信有任何可以可靠地执行代码的代码做到这一点考虑:?

Field myFirstNameField = myCustomer.FirstName; 
myFirstNameField.Value = "X"; 

应在属性名是什么在此领域的单个实例,现在有两个独立的范围内的两个标识的或许有给拉了回来标识符引用您的对象的列表(的方式但如果可以的话,我不知道怎么做),但你会如何选择你想要哪一个?这是,如果你想实现PROPERT我建议使用一个构造函数“注入”的属性名,其原因y以这种方式更改通知。

0

有办法做你想做的,但他们都不是特别优雅或万无一失。

举例来说,你可以走在你的领域类的调用堆栈时,您检测和错误情况,并假设它是通过现场的从对象检索总是调用。这可能并不总是正确...

或者,你可以使用LINQ表达式初始化时,它的字段的名称传递到Field对象。 Marc Gravell有technique that's described here。就你而言,这可能并不完美。

最终,你可能有一个更简单,更复杂的实施富裕。

最后,你应该考虑这是否真的是复制的情况而不是不必要的耦合。为什么从你的代码发出的消息要与其中的属性或方法名称绑定?如果你需要在将来的某个时候将你的代码国际化,该怎么办?如果客户希望消息更清晰或更紧密地与业务领域保持一致,您会愿意重构代码以适应这些变化吗?

+0

走栈不起作用,因为值属性不是通过“named”属性访问的。这已经从堆栈中弹出,并且您无法从属性中访问它。另外,我认为关键是实施INotifyPropertyChanged来为它填充事件arg。 – 2009-06-16 02:43:33

0

这里是要解决什么你正在尝试做的一种方式:

 Customer c1 = new Customer(); 
     c1.FirstName.Value = "Me"; 

     Type t = c1.GetType(); 
     MemberInfo[] infos = t.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance); 

     foreach (MemberInfo info in infos) 
     { 
      if (info.MemberType.ToString().Contains("Field")) 
      { 
       Console.WriteLine("Found member {0} of type {1}", info.Name, info.MemberType); 
      } 
     } 

但我同意在使用模板化的解决方案将是更好的。

0

你想要做的是不是真的有可能,你有它的设计方式。看起来应该是这样,但这两个类别之间确实没有多少关系。

我只需要添加name属性并将其更改为泛型类,因为步行堆栈在所有上效率都不高,但要尝试回答您的问题,您可以这样做以获得大部分所要求的内容。 ..

如果您包含字段类并且创建了实际类型的属性,您可以通过遍历堆栈来获取属性名称(并且如果添加了许多其他人提出的使其成为泛型类的更改,它会使它成为一个更好的解决方案,因为你不需要施放任何东西)。

如果你写的客户类是这样的:

public class Customer 
{ 
    private Field _firstName = new Field(typeof(string), 20); 

    public string FirstName 
    { 
     get 
     { 
      return _firstName.Value as string; 
     } 
     set 
     { 
      _firstName.Value = value; 
     } 
    } 
} 

这将允许你走栈在你的领域类来获取调用方法名(在这种情况下属性)

string name = "<unknown>"; 
StackTrace st = new StackTrace(); 
name = st.GetFrame(1).GetMethod().Name; 

名称现在将具有值

set_FirstName

所以你只需要剥离set_ off来获取属性名称,但是如果你使用的是INotifyPropertyChanged事件,你仍然会遇到问题,因为发件人将是Field对象,而不是客户对象。

0

我现在确信我无法从这里到达那里。在我看来,如果有一个Field类型的属性,应该很容易询问Field的属性名称。很明显,我会努力过于聪明,以避免一点点工作。我将在构造函数中添加一个属性名称。该字段永远不会在一个类之外创建,因此如果属性名称更改,必须在两个位置进行更改可能不方便。我并不担心将属性名称改为客户友好型或全球化,因为错误是指其他程序员试图将字符串放在一个字段中(或将太多数字转换为字符串)。我喜欢泛型的建议,但还没有制定出如何使用它,并仍然能够将所有字段放入列表中。最终,我想按特定顺序迭代列表,并构建一个类似于field1 = value1 | field2 = value2 | etc的单个字符串。