Анонимные классы, временные данные и коллекции анонимных классов - PullRequest
3 голосов
/ 10 августа 2010

Я новичок в анонимных классах, и сегодня я столкнулся с первым случаем, когда почувствовал, что действительно могу их использовать. Я пишу метод, который выиграл бы от хранения временных данных внутри класса, и поскольку этот класс не имеет никакого значения вне этого метода, использование анонимного класса, несомненно, имело смысл для меня (по крайней мере, в то время ).

После начала кодирования мне показалось, что мне придется пойти на некоторые уступки. Мне нравится назначать такие вещи, как вычисления, временным переменным, чтобы во время отладки я мог проверять биты вычислений одновременно в логических порциях. Затем я хочу назначить что-то более простое для конечного значения. Это значение будет в анонимном классе.

Проблема в том, что для краткой реализации моего кода с анонимными классами я бы хотел использовать LINQ. Проблема здесь в том, что я не думаю, что вы можете делать такие временные вычисления внутри оператора. или ты можешь?

Вот надуманный пример того, что я хочу сделать:

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

ПРИМЕЧАНИЕ: не пытайтесь запустить это, если вы фактически не добавляете ключи в Словарь :)

Создание анонимного класса в LINQ потрясающе, но заставляет меня выполнять вычисления в одну строку. Представьте, что калькулятор намного длиннее, чем я показал. Но это похоже на то, что я сделаю несколько словарных поисков, чтобы получить конкретные значения. Отладка может быть болезненной.

Использование конкретного класса позволяет обойти проблему использования временных переменных, но тогда я не могу все сделать кратко. Да, я понимаю, что я немного противоречив, когда говорю, что ищу краткость, и спрашиваю, можно ли сохранять временные переменные в моем операторе LINQ.

Я начинал пытаться создать анонимный класс при циклическом переходе по точкам, но вскоре понял, что у меня нет возможности его сохранить! Вы не можете использовать Список, потому что он просто теряет анонимность класса.

Может кто-нибудь предложить способ достичь того, что я ищу? Или что-то среднее? Я прочитал здесь несколько других вопросов о StackOverflow, но ни один из них не совпадает с моим.

Ответы [ 2 ]

5 голосов
/ 10 августа 2010

Если я вас правильно понял, проблема в том, что вы должны установить все свойства в одном выражении.Это определенно относится к анонимным типам.

Однако вам не нужно делать все это встроенным в этом выражении.Я бы предположил, что если ваши свойства основаны на сложных выражениях, вы разбиваете эти выражения на вспомогательные методы:

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

Это дает дополнительное потенциальное преимущество, заключающееся в том, что вы можете тестировать каждый из вспомогательных методов по отдельности, еслиВы фанат тестирования белого ящика (я).

Это не поможет избежать присутствия в одном большом выражении инициализатора анонимного типа, но это означает, что работа будет разбита.

1 голос
/ 10 августа 2010

Анонимные классы действительно предназначены для упрощения работы с лямбдами, не в последнюю очередь LINQ. То, что вы пытаетесь сделать, звучит гораздо более подходящим для вложенного частного класса. Таким образом, только ваш класс действительно знает о вашем временном классе. Попытка обойти анонимные классы только усложняет ваш код.

...