7

Lets say you have a Class with 300 properties with no backing variables, each of those properties returns a decimal/double.

Example:

public decimal MathValue { get; set; }

Now you decided that each one of those values should be rounded.

I am looking for the simplest way to refactor this without having to rewrite all of those properties.

Something, of this equivalent that actually works :D:

public decimal MathValue { get {return Math.Round(MathValue);} set; }
2
  • You may want to think about coming back to this class later and re-factoring out some properties into nice clean classes so you can avoid such a mess. Commented Dec 2, 2009 at 0:15
  • lol, complete over exaggeration on my part I was just curious how someone can get around such a thing :) Commented Dec 3, 2009 at 14:55

8 Answers 8

6

No. If you need any custom logic in either getter or setter, you cannot use auto-properties.

Sign up to request clarification or add additional context in comments.

5 Comments

And there is absolutely no way of overriding this logic or solution around this without declaring a variable for each one?
An alternative would be to write a command line util (or preferably use T4) to generate your 300 properties for you from some other file/data.
You could use something like PostSharp to rewrite your properties post-compile. Probably isn't worth it for something as trivial as that, however.
I was thinking of this as more of a theoretical question then a brute force one... I am open to any ideas that could help me work around the problem, with minimal code.
I don't see how this answers his question.
5

You could create a new value type that pretends to be a decimal, but returns the rounded value. Something like this:

struct RoundedDecimal
{
    public decimal Value { get; private set; }

    public RoundedDecimal(decimal value) : this()
    {
        this.Value = value;
    }

    public static implicit operator decimal(RoundedDecimal d)
    {
        return Math.Round(d.Value);
    }
}

Each property in your class should be of type RoundedDecimal instead of decimal.

8 Comments

I'd call this Int96, because that's what it effectively is.
This is a great way to pull it off without breaking the interface.
Actually it would probably be better if you made a public readonly field called Value and round it in the constructor.
I am liking this solution the most so far. :)
This would be so easy to implement as well. ctrl-h "decimal" "RoundedDecimal"
|
5

Easiest way to refactor the code? Here's what I would do:

  1. Open Notepad++ (get it if you don't have it)
  2. Copy/paste all properties of the class into a blank text area.
  3. Place the cursor at the start of the first line: public decimal MathValue1 { get; set; }
  4. Start recording a macro (click the record button on the toolbar)
  5. hold down ctrl+right arrow (called "word right") 3 times to put the cursor at the beginning of the property name.
  6. do shift+ctrl+right arrow 1 time and do a Copy to put the name of the property in the clipboard
  7. word right 3 more times to put the cursor after the "get"
  8. delete the semi-colon after the get and start typing " { return Math.Round(_"
  9. do a Paste 10 type "); }"
  10. word right 2 more times to put the cursor after the "set"
  11. delete the semi-colon after the set and start typing " { _"
  12. do a Paste
  13. type " = value; }
  14. press the End key to get the cursor to the end of the line
  15. press the right arrow key to get the cursor to the beginning of the next line.
  16. press the stop button to end your macro (square button on the toolbar)
  17. Click the "Run a macro multiple times" button (a double-arrow icon on the toolbar) and say "Run until the end of file"
  18. Copy/paste the resulting text back into your class to replace the original property definitions.

Now you'll need to define a set of corresponding private variables that begin with an underscore but otherwise have the same names as the properties. Start with a fresh copy of the properties from your class and perform a similar set of steps as described above.

My assumption is that each line starts with 2 tabs and there are no empty lines between properties.

Rather than having each property call Math.Round, you may want to consider defining your own utility function that they all call so that if you need to change it again, you can just change it in one place.

1 Comment

+1 - Never would have though of this. He may want to hit ctrl-k-d before he does it so VS will clean up all the white space.
1

You could create a derivative of this class that overrides the gets and returns the rounded values. You would then need to modify the base property to be virtual. But that would allow you to define the get without defining the set and using auto properties.

public class Base
{
    public virtual decimal MathValue { get; set; }
}

public class Derived : Base
{
    public override decimal MathValue
    {
        get { return Math.Round(base.MathValue); }
    }
}

4 Comments

How could this be accomplished? I would like to override the gets themselves, if that is possible. Could you maybe show me an example?
Sure. I also clarified that the whole property needs to be virtual, not just the getter.
Inheritance seems like overkill for this.
With strict regard to the question "is it possible to get around defining get without defining set", I disagree. With broader regard to his overall scenario, this approach also gives him a rounded and un-rounded version of the class. The resulting class names can then have better semantics. However, I agree that unless he can reap these particular benefits, this might be overkill.
0

But what happens if the client doesn't want a rounded value? ie, some new client code sets a decimal and expects that "exact" value back?

If some clients really need the output of the property call to be rounded, then the client should handle this and leave your class alone.

1 Comment

Well then in that case the get can just be left alone :) for that particular property... the only thing I want to do is avoid declaring a variable for each property.
0

One option is to use aspect-oriented programming to intercept the property invocations on return and round the return value before passing control back to the caller.

Comments

0

Visual Studio used to have a built-in "prop" code snippet that would generate something like the following code:

    private decimal _MathValue;

    public decimal MathValue
    {
        get { return _MathValue; }
        set { _MathValue = value; }
    }

That would get you most of the way to a complete solution, but since VS 2008 it now generates the automatic property version:

    public decimal MathValue { get; set; }

I haven't tried this yet, but here is a suggestion for creating your own code snippet to get the VS 2005 version of the "prop" code snippet back:

2 Comments

I am more interested in a way around the code rather then to manually refactor everything in the class even if I have a snippet that does it for me.
Ah, yes I should have read your question more closely. I see that you are in a situation where you already have hundreds of properties defined, so this doesn't really help you all that much.
0

You could do this with PostSharp or some other .NET-based AOP framework. Here is the MethodExecutionEventArgs.ReturnValue property that says it can be used to "modify the return value..."

This will do it:

[Serializable]
public class RoundingAttribute : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionEventArgs eventArgs)
    {
        base.OnExit(eventArgs);
        eventArgs.ReturnValue = Math.Round((double)eventArgs.ReturnValue, 2);
    }
}

class MyClass
{
    public double NotRounded { get; set; }

    public double Rounded { [Rounding] get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var c = new MyClass
                {
                    Rounded = 1.99999, 
                    NotRounded = 1.99999
                };

        Console.WriteLine("Rounded = {0}", c.Rounded); // Writes 2
        Console.WriteLine("Not Rounded = {0}", c.NotRounded);  // Writes 1.99999
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.