1

I have a custom class (call i Field) that that implements several properties. One of the properties is MaximumLength that spcifies the maximum length that the value can be. The Value property is an object so i can be set to string, int, double, etc. Then I have a class that has multiple properties of type Field in it. All the Field properties are initialized in the constructor and only the Field.Value property can be written to. What I want to do is throw an error if the an attempt is made to set the Field.Value to a value that is too long for the field and implement INotifyPropertyChanged. My problem is the Value property is a member of the generic Field class and I do not know how to get the name of the property inside that class.

an example:

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

Hopefully that is clear enough.

smok1
  • 2,940
  • 26
  • 35
jac
  • 9,666
  • 2
  • 34
  • 63
  • The closest you'll get to this is contextual access of the actual property, the field itself won't know its origin, after all- its just an instance.. the contextual thing- ie.. im accessing this property to get this field, which then opens up reflection would be very restrictive - similar systems to this like WPF's dependency system prove that in this scenario there isn't really a better way than simply duplicating the name in string form.. alternatively you could look into aspect orientated systems like postsharp, which will let you do compile time rewriting of the IL. – meandmycode Jun 16 '09 at 14:31

9 Answers9

0

I'm convinced now that I can't get there from here. In my mind it seemed if a had a property of type Field it should be simple to ask the Field what is your property name. It's obvious I would be trying to be too clever to avoid just a little work. I am going to add a property name in the constructor. The field will never be created outside of one class so it may be inconvenient to have to make a change in two places if a property name changes. I'm not really worried about making the property name customer friendly or globalization because the error is meant for other programmers trying to put too long of a string, (or too large a number converted to a string) into a field. I like the generics suggestion, but haven't worked out how to use it and still be able to put all the fields into a list. Ultimately I want to iterate the list in a specific order and build a single string similar to field1=value1|field2=value2|etc.

jac
  • 9,666
  • 2
  • 34
  • 63
0

Why not simply extend the Field class to also hold a name, and pass it along in the constructor?

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

The Field class would look like this:

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);
         }
      }
   }
}
Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • I've thought about that, but it seems redundant to me since the property has a name, and an added step if I or someone else starts renaming properties. I may fall back on that though if I can't get another method worked out. – jac Jun 15 '09 at 20:12
0

Agree w/Fredrik.

However, you should also probably consider using generics if you want to have some type-safety in your Field data, i.e. redefine your Field class as Field where you store T _value instead of an object.

I also assume that you'll have to have different algorithms for length checking for the different types (e.g. strings vs. numerics, etc.) as well as null value checking, etc.

HTH.

micahtan
  • 18,530
  • 1
  • 38
  • 33
0

I agree with the above. The field should have its own Name property that is set with construction.

I disagree with implementing this using generics. Type safety is a fine goal, but you will probably need to also be able to store a collection/list of Field objects of disparate types.

Generics won't let you do that without the generic class also implementing a common interface.

You could implement a common interface, but then you would need Value to be of type object, not type <T>, so you lose the performance benefits of generics.

richardtallent
  • 34,724
  • 14
  • 83
  • 123
  • I agree that if a collection of Field's is needed, then perhaps generics is not the way to go, or certainly interfaces would need to be added to make the generic implementation work. However, if Field is simply being used to encapsulate some validation logic, then I believe generics could be suitable and preferred. – Timothy Carter Jun 15 '09 at 20:47
0

I believe this does what you want to happen. I added generics to the class, instead of using object, to add type safety. I also added an explicit exception type so it would compile, but the Exception type could be anything that takes a string.

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

EDIT:

Responding to your comment: "I've thought about that, but it seems redundant to me since the property has a name, and an added step if I or someone else starts renaming properties. I may fall back on that though if I can't get another method worked out."

I believe what you're asking for is some sort of reflection code that will retrieve the name of the Property within Customer, when notifying the property change from Field. I don't believe there is any code that can reliably do this. Consider:

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

What should the property name be here? The single instance of Field, now has two identifier's within two separate scopes. Perhaps there's a way to pull back the list of identifiers referencing your object (though if you can, I don't know how), but how would you choose which one you want? This is the reason I would suggest using a constructor to "inject" the property name, if you want to implement the PropertyChangeNotification in this manner.

Timothy Carter
  • 15,459
  • 7
  • 44
  • 62
0

There are ways to do what you want, but none of them are particularly elegant or foolproof.

For instance, you could walk the call stack in your Field class when you detect and error condition, and assume that it is always invoked through the retrieval of a Field from an object. This will probably not always be true...

Alternatively, you could use the LINQ expressions to pass the name of the field into the Field object when initializing it. There's a technique that's described here by Marc Gravell. In your case, this may not be perfect either.

Ultimately, you are probably better off with a simpler, less complex implementation.

Finally, you should consider whether this is really a case of duplication rather than unnecessary coupling. Why should the messages emitted from your code be tied to the names of properties or methods in it? What if you need to internationalize your code at some future point? What if customers want the messages to be clearer or more closely aligned the business domain ... are you willing to refactor your code to accommodate such changes?

Community
  • 1
  • 1
LBushkin
  • 129,300
  • 32
  • 216
  • 265
  • walking the stack will not work because the value property is not accessed through the "named" property. That is already popped off the stack and you have no access to it from within the property. Also, I think that the point is the implementation of INotifyPropertyChanged to populate the event arg for it. – Brian ONeil Jun 16 '09 at 02:43
0

Here is a way to solve what you are trying to do:

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

But I agree that using a templated solution would be better.

Noam
  • 197
  • 1
  • 4
  • 13
0

What you want to do is not really possible the way that you have it designed. It seems like it should be, but really there isn't much of a relationship between the two classes.

I would just add the name property and change it to a generic class because walking stack is not efficient at ALL, but to try to answer your question you could do this to get most of what you are asking for...

If you contained the field class and made the properties of the actual type you could do it walking the stack to get the property name (and if you added the change that many others have suggested to make it a generic class, it would make it an even better solution because you wouldn't need to cast anything).

If you wrote the customer class like this:

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

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

This would allow you to walk the stack to get the calling method name in your field class (in this case property)

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

name would now have the value

set_FirstName

so you would just have to strip the set_ off it to get the property name, but if you are using the INotifyPropertyChanged event, you are still going to have a problem because the sender is going to be the Field object and not the Customer object.

Brian ONeil
  • 4,229
  • 2
  • 23
  • 25
0

It seems to me that, yes, there might be a way using reflection to get to this information, but I'm not certain that this will be a foolproof method, and might expose you to a more cumbersome debugging phase later on. It just seems excessively... clever.

In my opinion, and based on what I've implemented in the past, Fredrik and Micahtan are pointing you in the correct direction. Your Field class should implement a Name property, set on instantiation. One reason that I can point to for this being a good idea is that this is the way that Microsoft does this very same thing. If you look at the generated code for any of the visual designers, controls implement a Name property that is set by the designer. If there were a foolproof way to do this under the covers, you'd have to believe that this would be done.

An additional boon to using a Name property is that it allows for you to come up with an "English" translation for your properties. i.e. "First Name" instead of "FirstName" this is a friendlier approach to the user interface, and decouples the user from your implementation (to overload the word "decouple").

Tom Moseley
  • 591
  • 7
  • 15