次の方法で共有


Properties as objects

I was thinking about two items from Object Thinking:

  1. Everything is an object.
  2. Every object should provide events for when its attributes change.

There's the idea that objects can collaborate without top-down control, if they provide & register events with each other.

My first reaction to #2 is that it violates YAGNI. But in the interest of extremism, I'll let it ride for now.

The standard way to do #2 is something like:

      class Customer

      {

            int _age;

            public int Age

            {

                  get {

                        return this._age;

                  }

                  private set

                  {

                        this._age = value;

                        this.Age_OnChange(this, null);

                  }

            }

            public event System.EventHandler Age_OnChange;

      }

Now, you already know how much I love properties. I’m annoyed when I read this code, because I use 15 lines of text to express what should be a very basic idea.

There’s also a bunch of smelly duplication. Not only does the word “age” appear 6 times, but 2 of them are carefully concealed in a bigger name (“Age_OnChange”), which is quite easy to miss in later code changes. Yuck.

So, what if we apply #1? What if we make an object that represents a property? It would look like:

      class Property<T>

      {

            public Property(T t) { ... }

            public event System.EventHandler OnChanged;

            public T Value { ... }

      }

Well, I tried to implement it, and ran into some problems. The biggest is that I can’t state who has permissions to set the property. With the seed code above, only the containing class has write access to the value.

Here’s the best I came up with:

      class Property<T>

      {

            private T _value;

            public event System.EventHandler OnChanged;

            public T Value

            {

                  get { return this._value; }

                  set

                  {

                        this._value = value;

                        this.OnChanged(this, null);

                  }

            }

      }

      class Customer

      {

            public Property<int> age = new Property<int>();

            static void Main(string[] args)

            {

                  Customer customer = new Customer();

                  customer.age.OnChanged += delegate { Console.WriteLine("age changed"); };

                  customer.age.Value = 9;

            }

      }

I don’t think I can do any better. Got any ideas?

Comments

  • Anonymous
    June 14, 2004
    It's exactly the idea of the PropertyHolder class that supports the Observer design pattern (see "Design Patterns in C#" book by Steven John Metsker, pp. 103-107 but also some pages before - http://www.oozinoz.com/dpcs.htm). There, Steve uses C# 1.0 (no generics) with a little help of reflection.

    If you download the source code, take a look at this folder: oozinozappShowBallistics4

  • Anonymous
    June 14, 2004
    To me the problem is that there's a difference between fields and properties to the system.

    My preference would be to have the ability to constrain the values of variables, not just the types. If something would try to set the variable to an unacceptable value then an exception would be thrown. However it seems rather annoying to have to write this out in a general format and not a format specifically designed for it.

    Eiffel has something like this and I'd like to see C# have something like this too.

    Orion Adrian

  • Anonymous
    June 14, 2004
    jaybaz: don't forget to check if the value has actually changed before assigning to the private member and calling OnChanged in the set part:

    set
    {
    if (_value != value)
    {
    _value = value;
    OnChanged(this, null);
    }
    }

  • Anonymous
    June 14, 2004
    Out of curiosity: why do you prefix private members with "this"?

  • Anonymous
    June 14, 2004
    'this' is used to properly distinguish exactly which variable you're using. If you have

    class Person
    {
    public string name;

    public Person(string name)
    {
    this.name = name;
    }

    the code requires 'this.' to properly clarify to the compiler just which 'name' is set. If a prefix such as '_' is used on fields, 'this.' is unnecessary, but often used anyway for the sake of clarity.

  • Anonymous
    June 14, 2004
    The comment has been removed

  • Anonymous
    June 15, 2004
    Mike: It's a design question that I never answered. I suppose with a name like "OnChanged" you're right; that the behavior I wrote should be called "OnSet".

  • Anonymous
    June 15, 2004
    Ron: I especially like your suggestion to ExtractMethod.

    I'm dumping the updated code into this comment:

    class Property<T>
    {
    private T _value;

    public delegate void SetEventHandler(object sender, T newValue);

    public event SetEventHandler OnSet;
    public T Value
    {
    get { return this._value; }
    set
    {
    this._value = value;
    FireOnSet();
    }
    }
    void FireOnSet()
    {
    SetEventHandler temp = this.OnSet;
    if (temp != null)
    {
    temp(this, this._value);
    }
    }
    }

  • Anonymous
    June 15, 2004
    Jay,

    1. I would have included the old value in the event for reference's sake.
    2. The only problem with this is that there's no such thing as a default property, like in VB. I have to write customer.age.Value instead of doing customer.age. What happened if T was Nullable<int>? This would cause the code to be customer.age.Value.Value, which is rather ugly (yes, I know you don't have to explicitly call Value on a nullable type). customer.age.OnChanged is cool, though. :)

  • Anonymous
    June 15, 2004
    In these explorations I keep seeing .Value pop up. I'm afraid it's unavoidable, and my code will soon be littered with .Value.Value.Value.Value in the name of readability!

  • Anonymous
    June 15, 2004
    It would be nice if C# supported the VB concept of a default property. An attribute could be used to indicate which property use.
    This would get the syntax back to what you would expect: customer.age = 9;

    class Property<T>
    {
    private T _value;

    [DefaultProperty()]
    public T Value
    {
    get { return this._value; }
    }
    }

  • Anonymous
    June 23, 2004
    One of my favorite bloggers as of late is Jay Bazuzi (rss), who works on the Visual C# IDE team at Microsoft. Give him a read; he's got some posts that are sure to melt your brain, such as "no...

  • Anonymous
    June 09, 2009
    PingBack from http://menopausereliefsite.info/story.php?id=207

  • Anonymous
    June 12, 2009
    PingBack from http://cellulitecreamsite.info/story.php?id=621