views:

68

answers:

2

I'm new to anonymous classes, and today I think I ran into the first case where I felt like I could really use them. I'm writing a method that would benefit from storing temporary data inside of a class, and since that class doesn't have any meaning outside of that method, using an anonymous class sure made sense to me (at least at the time it did).

After starting on the coding, it sure seemed like I was going to have to make some concessions. I like to put assign things like calculations to temporary variables, so that during debugging I can verify bits of calculations at a time in logical chunks. Then I want to assign something simpler to the final value. This value would be in the anonymous class.

The problem is that in order to implement my code with anonymous classes concisely, I'd like to use LINQ. The problem here is that I don't think you can do such temporary calculations inside of the statement. or can you?

Here is a contrived example of what I want to do:

namespace AnonymousClassTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        ObservableCollection<RectanglePoints> Points { get; set; }

        public class RectanglePoints
        {
            public Point UL { get; set; }
            public Point UR { get; set; }
            public Point LL { get; set; }
            public Point LR { get; set; }
        }

        public class DontWantThis
        {
            public double Width { get; set; }
            public double Height { get; set; }
        }

        private Dictionary<string,string> properties = new Dictionary<string,string>();
        private Dictionary<string,double> scaling_factors = new Dictionary<string,double>();

        private void Sample()
        {
            // not possible to do temp variables, so need to have
            // longer, more unreadable assignments
            var widths_and_heights = from rp in Points
                                     select new
                                     {
                                        Width = (rp.UR.X - rp.UL.X) * scaling_factors[properties["dummy"]],
                                        Height = (rp.LL.Y - rp.UL.Y) * scaling_factors[properties["yummy"]]
                                     };

            // or do it in a for loop -- but then you have to use a concrete
            // class to deal with the Width and Height storage
            List<DontWantThis> other_widths_and_heights = new List<DontWantThis>();
            foreach( RectanglePoints rp in Points) {
                double base_width = rp.UR.X - rp.UL.X;
                double width_scaling_factor = scaling_factors[properties["dummy"]];
                double base_height = rp.LL.Y - rp.UL.Y;
                double height_scaling_factor = scaling_factors[properties["yummy"]];

                other_widths_and_heights.Add( new DontWantThis 
                                              { 
                                                  Width=base_width * width_scaling_factor,
                                                  Height=base_height * height_scaling_factor
                                              });
            }

            // now we want to use the anonymous class, or concrete class, in the same function
            foreach( var wah in widths_and_heights)
                Console.WriteLine( String.Format( "{0} {1}", wah.Width, wah.Height));
            foreach( DontWantThis dwt in other_widths_and_heights)
                Console.WriteLine( String.Format( "{0} {1}", dwt.Width, dwt.Height));
        }

        public Window1()
        {
            InitializeComponent();
            Points = new ObservableCollection<RectanglePoints>();
            Random rand = new Random();
            for( int i=0; i<10; i++) {
                Points.Add( new RectanglePoints { UL=new Point { X=rand.Next(), Y=rand.Next()  },
                                                  UR=new Point { X=rand.Next(), Y=rand.Next()  },
                                                  LL=new Point { X=rand.Next(), Y=rand.Next()  },
                                                  LR=new Point { X=rand.Next(), Y=rand.Next()  }
                                                } );
            }

            Sample();
        }
    }
}

NOTE: don't try to run this unless you actually add the keys to the Dictionary :)

The creation of the anonymous class in LINQ is awesome, but forces me to do the calculation in one line. Imagine that the calc is way longer than what I've shown. But it is similar in that I will do some Dictionary lookups to get specific values. Debugging could be painful.

The usage of a concrete class gets around this problem of using temporary variables, but then I can't do everything concisely. Yes, I realize that I'm being a little contradictory in saying that I'm looking for conciseness, while asking to be able to save temp variables in my LINQ statement.

I was starting to try to create an anonymous class when looping over Points, but soon realized that I had no way to store it! You can't use a List because that just loses the entire anonymity of the class.

Can anyone suggest a way to achieve what I'm looking for? Or some middle ground? I've read a few other questions here on StackOverflow, but none of them are exactly the same as mine.

+3  A: 

Assuming I understand you correctly, the problem is that you have to set all the properties in a single expression. That's definitely the case with anonymous types.

However, you don't have to do it all inline in that expression. I would suggest that if your properties are based on complex expressions, you break those expressions out into helper methods:

var complex = new {
      First = ComputeFirstValue(x, y),
      Second = ComputeSecondValue(a, b)
      ...
};

This has the additional potential benefit that you can unit test each of the helper methods individually, if you're a fan of white-box testing (I am).

This isn't going to avoid there being in one big anonymous type initializer expression, but it means the work will be broken up.

Jon Skeet
@Jon you did understand me correctly. Thanks for confirming this for me, and your idea sounds great!
Dave
+1  A: 

Anonymous classes are really intended to simplify stuff dealing with lambdas, not least LINQ. What you're trying to do sounds much more suited to a nested private class. That way, only your class really knows about your temp class. Trying to muck around with anonymous classes seems only to complicate your code.

Pontus Gagge