代码之家  ›  专栏  ›  技术社区  ›  jac

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

  •  1
  • jac  · 技术社区  · 15 年前

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

    一个例子:

    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 回复  |  直到 15 年前
        1
  •  0
  •   Tom Moseley    15 年前

    在我看来,是的,可能有一种使用反射来获取这些信息的方法,但是我不确定这将是一种简单的方法,并且可能在以后将您暴露在一个更麻烦的调试阶段。似乎太过分了…聪明的。

    在我看来,根据我过去的实践,弗雷德里克和米卡坦是在向你指明正确的方向。字段类应实现在实例化时设置的名称属性。我可以指出的一个原因是,这是一个好主意,这是微软做这个非常相同的事情的方式。如果查看为任何可视化设计器生成的代码,控件将实现由设计器设置的名称属性。如果有一种万无一失的方法可以做到这一点,那么你就必须相信这是可以做到的。

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

        2
  •  0
  •   Fredrik Mörk    15 年前

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

    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);
             }
          }
       }
    }
    
        3
  •  0
  •   micahtan    15 年前

    同意W/Fredrik。

    但是,如果希望在字段数据中具有某种类型安全性,也可以考虑使用泛型,即将字段类重新定义为存储t值而不是对象的字段。

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

    Hth.

        4
  •  0
  •   richardtallent    15 年前

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

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

    如果没有泛型类也实现一个公共接口,泛型就不允许这样做。

    您可以实现一个公共接口,但是您需要的值是对象类型,而不是类型 <T> 因此,您将失去泛型的性能优势。

        5
  •  0
  •   Community Rick James    7 年前

    有很多方法可以做你想做的事,但是没有一种方法是特别优雅或简单的。

    例如,当检测到和错误条件时,可以在字段类中遍历调用堆栈,并假定始终通过从对象中检索字段来调用它。这可能并不总是真的…

    或者,在初始化字段对象时,可以使用LINQ表达式将该字段的名称传递给该字段对象。有一个 technique that's described here 马克·格雷弗尔。在您的情况下,这也可能不完美。

    归根结底,您最好使用更简单、更不复杂的实现。

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

        6
  •  0
  •   Timothy Carter    15 年前

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

    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 myFirstNameField = myCustomer.FirstName;
    myFirstNameField.Value = "X";
    

    这里的属性名应该是什么?现在,字段的单个实例在两个单独的范围内有两个标识符。也许有一种方法可以拉回引用对象的标识符列表(尽管如果可以,我不知道如何),但是您将如何选择所需的标识符?这就是我建议使用构造函数“注入”属性名的原因,如果您希望以这种方式实现propertychangenotification。

        7
  •  0
  •   Noam    15 年前

    以下是一种解决您尝试做的事情的方法:

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

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

        8
  •  0
  •   Brian ONeil    15 年前

    你想要做的并不是你设计它的方式。这似乎是应该的,但实际上这两个阶级之间没有太多的关系。

    我只需添加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_uu即可获得属性名,但如果您使用的是inotifyPropertyChanged事件,则仍然会出现问题,因为发送者将是字段对象而不是客户对象。

        9
  •  0
  •   jac    15 年前

    我现在确信我不能从这里到达那里。在我看来,如果a有一个字段类型的属性,那么很容易询问该字段您的属性名是什么。很明显,我太聪明了,不可能只做一点工作。我将在构造函数中添加一个属性名。字段永远不会在一个类之外创建,因此如果属性名发生更改,可能不方便在两个地方进行更改。我并不担心让属性名对客户友好或全球化,因为这个错误是为了让其他程序员尝试将太长的字符串(或转换为字符串的数字太大)放入字段。我喜欢这个仿制药的建议,但是我还没有弄清楚如何使用它,并且仍然能够将所有字段都放到一个列表中。最后,我希望按照特定的顺序迭代列表,并构建一个类似于field1=value1_field2=value2_等的字符串。