代码之家  ›  专栏  ›  技术社区  ›  Dave

匿名类、临时数据和匿名类的集合

  •  3
  • Dave  · 技术社区  · 14 年前

    我不熟悉匿名类,今天我想我遇到了第一个案例,我觉得我可以真正使用它们。我正在编写一个方法,它将从在类中存储临时数据中获益,而且由于该类在该方法之外没有任何意义,所以使用匿名类对我来说是有意义的(至少在它实现的时候)。

    在开始编码之后,我肯定会做出一些让步。我喜欢将诸如计算之类的赋值放到临时变量中,这样在调试期间,我可以一次在逻辑块中验证计算的位。然后我想给最终的值指定一个更简单的值。该值将在匿名类中。

    问题是为了用匿名类简洁地实现我的代码,我想使用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 回复  |  直到 14 年前
        1
  •  5
  •   Jon Skeet    14 年前

    假设我理解正确,问题是必须在一个表达式中设置所有属性。匿名类型的情况肯定是这样的。

    但是,您不必在该表达式中全部内联。我建议,如果您的属性基于复杂表达式,那么您可以将这些表达式分解为helper方法:

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

    如果您喜欢白盒测试(I am),那么您可以单独对每个助手方法进行单元测试,这有额外的潜在好处。

    这不会避免在一个大型匿名类型初始值设定项表达式中存在,但这意味着工作将被中断。

        2
  •  1
  •   Pontus Gagge    14 年前

    匿名类实际上是为了简化处理lambda的工作,尤其是linq。您要做的事情听起来更适合于嵌套的私有类。这样,只有你的班才真正了解你的临时班。试图与匿名类混在一起似乎只会使代码复杂化。