我需要一个多维数据结构,其中每个维度是在设计时已知的小列表.
在我的程序中的不同地方,我想要以强类型的方式访问不同维度的“切片”数据.
我把下面的一些示例代码放在了使用嵌套接口的二维示例中,但是我想象会在3D或4D中变得非常可怕.如@kvb所示,所需的样板代码将呈指数增长.
有没有人有更好的建议?我的意思是,保持代码需要简单/简短/容易理解,同时仍然保留以下行为的能力:
Data a = new Data(...) ... SomeMethodThatOnlyCaresAboutRedThings(a.Red) // takes a IBySize<T> ... SomeMethodThatOnlyCaresAboutBigThings(a.Big) // takes a IByColour<T> ...
这避免了这些方法必须知道数据结构与它们无关的部分,从而使它们更易于测试.
我以纯粹的颜色/大小为例,道歉,无意中误导了任何人这些选择是有意义的. T可以是一个简单的数据项,如浮点数或其他一些简单的数据结构.
public interface IByColour<T> { T Green { get; } T Red { get; } T Blue { get; } } public interface IBySize<T> { T Small { get; } T Big { get; } } internal class ByColour<T> : IByColour<T> { public T Green { get; private set; } public T Red { get; private set; } public T Blue { get; private set; } internal ByColour(T green,T red,T blue) { Green = green; Red = red; Blue = blue; } } internal class BySize<T> : IBySize<T> { public T Small { get; private set; } public T Big { get; private set; } internal BySize(T small,T big) { Small = small; Big = big; } } public class Data<T> : IByColour<IBySize<T>>,IBySize<IByColour<T>> { public IBySize<T> Green { get; private set; } public IBySize<T> Red { get; private set; } public IBySize<T> Blue { get; private set; } public IByColour<T> Small { get; private set; } public IByColour<T> Big { get; private set; } public Data(IBySize<T> green,IBySize<T> red,IBySize<T> blue) { Green = green; Red = red; Blue = blue; Small = new ByColour<T>(Green.Small,Red.Small,Blue.Small); Big = new ByColour<T>(Green.Big,Red.Big,Blue.Big); } }
解决方法
这听起来好像使用了一种很好的老式DataTable.然后,您可以使用Linq切片和骰子,而您想要的,并且由选择的列的不同组合创建的任何唯一类型由编译器自动生成. DataTable中的所有列都是强类型的,对它们的查询结果也是如此.此外,DataTable中的DataColumn可以有任何类型,包括复杂对象或您自己的枚举类型.
如果你想坚持一个更多的情绪/不变/ F#的做事方式,你可以使用一个数组或元组列表,类型1,类型2,类型N>,这与DataTable基本上是一样的.
如果你给出了更多的建模背景,我可以提供一个例子.我不知道你发布的代码是否应该代表衣服,图像(RGB颜色空间)或完全不同的东西.
[一小时后]嗯,没有OP的更新,所以我继续一个例子,我使用List< Tuple< x,y,..n>>并假定物品是服装物品.
// Some enums public enum Size { Small,Medium,Large } public enum Color { Red,Green,Blue,Purple,Brown } public enum Segment { Men,Women,Boys,Girls,Infants } // Fetches the actual list of items,where the object // item is the actual shirt,sock,shoe or whatever object static List<Tuple<Size,Color,Segment,object>> GetAllItems() { return new List<Tuple<Size,object>> { Tuple.Create(Size.Small,Color.Red,Segment.Boys,(object)new { Name="I'm a sock! Just one sock." }),Tuple.Create(Size.Large,Color.Blue,Segment.Infants,(object)new { Name="Baby hat,so cute." }),Color.Green,Segment.Women,(object)new { Name="High heels. In GREEN." }),}; } static void test() { var allItems = GetAllItems(); // Lazy (non-materialized) definition of a "slice" of everything that's Small var smallQuery = allItems.Where(x => x.Item1 == Size.Small); // Lazy map where the key is the size and the value is // an IEnumerable of all items that are of that size var sizeLookup = allItems.ToLookup(x => x.Item1,x => x); // Materialize the map as a dictionary the key is the size and the // value is a list of all items that are of that size var sizeMap = sizeLookup.ToDictionary(x => x.Key,x => x.ToList()); // Proof: foreach (var size in sizeMap.Keys) { var list = sizeMap[size]; Console.WriteLine("Size {0}:",size); foreach (var item in list) { Console.WriteLine(" Item: {{ Size={0},Color={1},Segment={2},value={3} }}",item.Item1,item.Item2,item.Item3,item.Item4); } } }