Click here to Skip to main content
15,867,488 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello all,
I'm having a (probably) very common problem. I'd Google, but I'm not sure what to Google for.
The problem I have is as follows:

I have a class with a few properties, say PropertyA, PropertyB and PropertyC.
When I set PropertyA I want PropertyB and PropertyC to get certain values (probably dependent on the new value of PropertyA). I know it's not a good practice to do a lot of work in a property setter, but I wouldn't know how to do it otherwise (I'm using WinForms binding).
So anyway, the code looks a little like this:
C#
public string PropertyA
{
   get
   {
      return _PropertyA;
   }
   set
   {
      _PropertyA = value;
      if (value != null)
      {
         this.PropertyB = this.GetPropertyBValue();
         this.PropertyC = this.GetPropertyCValue();
      }
      else
      {
         this.PropertyB = null;
         this.PropertyC = null;
      }
}
So far so good, but as I get more properties I tend to get more bugs. I forget to reset values, or the setter of PropertyC needs to set the value of PropertyD and the setter of PropertyD sets PropertyB (which we have already set in PropertyA), etc. After a while you might even get StackOverflowExceptions (for example I have a ProductionDate, a PreservableDate and a DaysPreservable property that each set each other).
I think you can guess the result. The code becomes unreadable and you're never quite sure which setters are triggered from which setters...
The only plus I see to this method is that it's quite easy to debug (of course it's to bad I need to debug it so much!).

Is there any method, perhaps a design pattern, that addresses this specific issue?
Thanks.
Posted
Comments
PIEBALDconsult 4-Nov-14 9:50am    
That seems a very poor design. Ideally, setting a property will affect only that one property and nothing else - for exactly the reasons you point out.
Sander Rossel 4-Nov-14 10:07am    
So how do I set property B if property A changes and all I have is my binding?
I know this solution has some problems, but I haven't heard anything better yet ;)
PIEBALDconsult 4-Nov-14 10:20am    
Perhaps you want something like an Initialize Method rather than settable properties. Possibly with optional parameters?
Sander Rossel 4-Nov-14 10:35am    
I'd need to call Initialize every time I set a Property? Anyway, my WinForms binding isn't going to call an Initialize method.
PIEBALDconsult 4-Nov-14 10:39am    
No, read-only properties.

There is nothing wrong with your code. Your problem is the matter of general design of your code. You hardly can find any miracle technique or design pattern, because the basic idea is very elementary, and your problem with cross-dependent properties is way too specific to the way you personally design your classes; it is not related to any commonly known problem.

Let's imagine we do pre-OOP programming. Would you try to Google for the problems "I have more and more functions, they all have different number of parameters, and many of the functions use several other functions; as I added more functions, the code becomes unreadable; for mutually depending functions, I even have stack overflow exceptions…"? Now think about this: how such situation can be different from yours? Not much. After all, all those properties are nothing but functions (with one input argument for setters and one return values for getters, plus one implicit "this" argument for both), only hidden behind the property reading and property assignment syntax.

Fix your bugs. Thoroughly plan your classes and their members. Decide what constraints are really important. Find out most important features and focus on them. Foresee the use of your classes. And so on.

—SA
 
Share this answer
 
Comments
Sander Rossel 4-Nov-14 10:32am    
Well I would say it's a common problem, although I understand your comments.
In my case I have a property Product, which has a default Quality. So when I set Product then the Product setter sets Quality. When I have Product and Quality I can calculate the default Price, so I need to calculate the price when either Product or Quality changes, except I know Product already sets Quality and Quality sets the Price, so now I won't set Price in Product because I let Quality handle that.
It seems like a common scenario that when the value of a property or a group of properties changes that other properties need to change which causes a sort of chain reaction.

So you say the 'general design' of my code is problematic. How would you handle this scenario?
I've been thinking to use the PropertyChanged event, but that would move almost all my business logic into a single event and I would need to work with strings, as that is what PropertyChanged gives me. At least it would make me say something like if (propertyName == "Product") { // set Quality } if (propertyName == "Product" || propertyName == "Quality") { // set Price }
And in that case I could maybe even keep a list with methods to execute. So in this case Price would be set twice (once by Product and once by Quality), but if Quality notices a SetPrice is already scheduled it could skip the SetPrice.
PIEBALDconsult 4-Nov-14 10:50am    
I don't think you're thinking OOP enough. :D This isn't really the place to have a discussion such as this.

"when I set Product then the Product setter sets Quality" Shouldn't the Product class _have_ Quality? Then whenever you need access the Product's Quality, you don't need to set it anywhere. If you want a different Quality, use a different Product.

I'm not sure what you are trying to do, but I picture something like the Properties of a Control in the VS Designer, look at the Font, things like Size aren't directly properties of the Control, they're properties of the Font.
Sander Rossel 4-Nov-14 11:05am    
In this case a Product can have different qualities. So the user has to pick one, but not change the actual (default) quality of the product. Ultimately I need to create a record that says the user picked product X with quality Y.
And stuff gets even more complicated... I have a property which comes from product, but after that the user can pick an order that can override that property. It's so bad that I have four sources for the same value! And some take precedence over the other. So the product says it's A, but the order says it's B, then there's an internal order that says it's B, but some assignment that says it's actually A. And these are actually customer specifications.
I had no say in the domain model, I'm just trying to work with it...
PIEBALDconsult 4-Nov-14 11:23am    
"I had no say in the domain model, I'm just trying to work with it..." My condolences.

"the user picked product X with quality Y" I would do that with subclassing.
abstract class Widget ...
class HighQualityWidget : Widget...
class LowQualityWidget : Widget...
Sander Rossel 4-Nov-14 11:39am    
I have no idea how I would do that (yes, I am very much aware of how inheritance works, but there's actually a property that indicates what qualities may be used, but only if the product is of a specific type, really you don't want to know). Ultimately there may be one quality (which I have to hard-code since it somehow doesn't fit in the model) or up to five qualities that the user can pick.
Next to that I don't know what I would get out of that... The user still needs two lookups, one to pick the product and one to pick the quality.
The proper way to overcome such a loop is define a variable for every property an set those variables instead of the property itself...
C#
private string _propA;
public string propA
{
  get {}
  set {
    _propB = value;
    _propC = value;
  }
}
Something along this line..

I also advise you to write a single method to do all the updates and call it from all the properties, instead of setting them in each and every setter...

[EDIT]
A second options I used once in a complex scenario is to set a dirty flag for the core property and compute dependent properties only upon request...
C#
private string _propA;
private bool _propAIsDiry;
public string propA
{
  get {}
  set {
    _propAIsDirty = true;
  }
}
public string propB
{
  get {
    if( _propAIsDiry) ComputePropB();

    return( _propB );
  }
  set {}
}
 
Share this answer
 
v2
Comments
Sander Rossel 4-Nov-14 10:11am    
Yes, I would make a single method, but not all properties need to set the same other properties. So there isn't really a single method...
Having flags like _updatingPropB and then saying if (!_updatingPropB) { ... } doesn't make the code much better. I now have even more variables to worry about!
In my particular case it's pretty easy since I can check if the current value is different from the new value, but it still doesn't feel good that I have to do that kind of thing.
Kornfeld Eliyahu Peter 4-Nov-14 12:11pm    
I added an other idea I had once for a much more complex scenario...
PIEBALDconsult 4-Nov-14 10:29am    
"set those variables instead of the property itself"
I disagree; there's a reason to have the getters and setters and even the class itself shouldn't access the fields directly.
Auto-implemented properties help enforce this restriction.
Maciej Los 4-Nov-14 10:52am    
Agree, i think the best idea in this case is to create OnChange event.
Kornfeld Eliyahu Peter 4-Nov-14 12:43pm    
Events inside the class for dependent property update? It would be a total waste! All you do is put your code behind a delegate that is far more expensive than a single method call!!!
Please, read my comment to the solution1->PIEBALDconsult comment.

Here is an idea:
How to: Implement Property Change Notification[^]
How to: Implement Events in Your Class[^]
 
Share this answer
 
Comments
Sander Rossel 4-Nov-14 11:08am    
Wouldn't that simply move my code from the setters and into the event handlers? Except that I've now created an extra layer of notifications...?
Maciej Los 4-Nov-14 11:20am    
In what manner?
Sander Rossel 4-Nov-14 11:24am    
Well instead of having a property like this
set
{
_MyProp = value;
// Do stuff...
}

I now get:
set
{
_MyProp = value;
// Raise event.
}

void OnMyPropChanged(object sender, EventArgs e)
{
// Do stuff...
}

So nothing would change, except that I'd put it in an event.
Except maybe the PropertyChanged event (ALL my properties raise PropertyChanged to update my bindings). Also see my comment to Sergey's answer.
Maciej Los 4-Nov-14 14:30pm    
Ok, thank you for explanation. At that point of view, you're right. I'll follow your comments.
Sergey Alexandrovich Kryukov 4-Nov-14 12:44pm    
Your concern may make some sense. Again, it depends on general design of your code. However, it is possible that you not quite understand the complex idea of complexity. If you add extra level, it may make code simpler. It depends not on amount of code, but on the abstraction. The idea behind events: the event objects are agnostic to the object receiving events. This becomes even more apparent in case of message-based event-oriented programming: the message is sent "somewhere", without regards to the nature of the object receiving it.

But there is the opposite misconception, a whole anti-pattern of design: bones without meat, plumbing-only code; one layer merely takes data and passes to another layer, and so on. I so such code, this is nasty. Think carefully if you are not doing something like that. Maybe you mostly move data between properties. The criteria for sensible design is: each line should carry some knowledge, on one abstraction level or another. Think about it.

However, I'm not sure this idea can help you. I think considering patterns is premature here. You need to do the general code design, analyze the application aspects first.

—SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900