如何获取myClass类型的属性的名称?

时间:2009-06-15 20:06:12

标签: c# .net

我有一个实现多个属性的自定义类(调用i Field)。其中一个属性是MaximumLength,它指定值的最大长度。 Value属性是一个对象,所以我可以设置为string,int,double等。然后我有一个类,其中包含多个Field类型的属性。所有Field属性都在构造函数中初始化,并且只能写入Field.Value属性。我想要做的是,如果尝试将Field.Value设置为对于该字段而言太长的值并实现INotifyPropertyChanged,则抛出错误。我的问题是Value属性是泛型Field类的成员,我不知道如何在该类中获取属性的名称。

一个例子:

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

希望这很清楚。

9 个答案:

答案 0 :(得分: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);
         }
      }
   }
}

答案 1 :(得分:0)

同意Fredrik。

但是,如果您希望在Field数据中使用某些类型安全性,也可以考虑使用泛型,即将Field类重新定义为存储T _value而不是对象的Field。

我还假设您必须使用不同的算法来检查不同类型的长度(例如字符串与数字等)以及空值检查等。

HTH。

答案 2 :(得分:0)

我同意上述内容。该字段应具有自己的Name属性,该属性使用构造设置。

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

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

您可以实现一个通用接口,但是您需要Value为object类型,而不是类型< T> ,因此您将失去泛型的性能优势。

答案 3 :(得分:0)

我相信这会做你想要发生的事。我在类中添加了泛型,而不是使用object,以添加类型安全性。我还添加了一个显式异常类型,因此它可以编译,但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中的Property的名称。我不相信有任何代码可以可靠地执行此操作。考虑:

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

这里的物业名称应该是什么? Field的单个实例现在在两个单独的范围内有两个标识符。也许有一种方法可以撤回引用你的对象的标识符列表(尽管你可以,我不知道如何),但你会如何选择你想要的?这就是我建议使用构造函数“注入”属性名称的原因,如果你想以这种方式实现PropertyChangeNotification。

答案 4 :(得分:0)

有办法做你想做的事,但没有一个是特别优雅或万无一失。

例如,当您检测到错误条件时,可以在Field类中遍历调用堆栈,并假设始终通过从对象中检索Field来调用它。这可能并非总是如此......

或者,您可以使用LINQ表达式在初始化时将字段名称传递给Field对象。 Marc Gravell有一个technique that's described here。在你的情况下,这可能也不完美。

最终,你可能会更简单,更简单的实现。

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

答案 5 :(得分: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);
            }
        }

但我同意使用模板解决方案会更好。

答案 6 :(得分: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对象而不是客户对象。

答案 7 :(得分:0)

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

在我看来,根据我过去实施的内容,Fredrik和Micahtan指出了你正确的方向。 Field类应该实现一个Name属性,在实例化时设置。我可以指出这是一个好主意的一个原因是,这是微软做同样事情的方式。如果查看任何可视化设计器的生成代码,控件将实现由设计器设置的Name属性。如果有一种万无一失的方法可以做到这一点,你必须相信这样做。

使用Name属性的另一个好处是,它允许您为您的属性提供“英语”翻译。即“First Name”而不是“FirstName”,这是一种更友好的用户界面方法,并将用户与您的实现分离(使“decouple”这个词重载)。

答案 8 :(得分:0)

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