昨天我使用.NET 4.0中的新
我在几个类中都有这个结构,所以我想尽可能地将这些列表的同步设置为通用。但由于列表的内部类型可能会有所不同,这并不像我最初想象的那么简单。但是,今天的英雄登场了:动态类型 :)
我编写了以下帮助类,它可以接受一个列表集合(任何类型的列表)和每个列表的默认值:
dynamic
类型编写了我的第一行代码。我发现这个场景很有用,它是这样的:我有一个类,保存着多个值列表。这可以是List<string>
、List<bool>
、List<int>
或者任何类型的列表。这些列表的使用方式是,我向其中一个或多个列表添加一个值。然后我“同步”它们,使它们最终达到相同的长度(那些太短的列表会填充默认值)。然后我继续添加更多的值,再次同步等等。目标是,在一个列表中的任何索引处的项与另一个列表中相同索引处的项相关联。(是的,这可能可以通过将所有内容包装在另一个类中来更好地解决,但在这种情况下不是重点。)我在几个类中都有这个结构,所以我想尽可能地将这些列表的同步设置为通用。但由于列表的内部类型可能会有所不同,这并不像我最初想象的那么简单。但是,今天的英雄登场了:动态类型 :)
我编写了以下帮助类,它可以接受一个列表集合(任何类型的列表)和每个列表的默认值:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Foo.utils
{
public class ListCollectionHelper
{
/// <summary>
/// Takes a collection of lists and synchronizes them so that all of the lists are the same length (matching
/// the length of the longest list present in the parameter).
///
/// It is assumed that the dynamic type in the enumerable is of the type Tuple<ICollection<T>, T>, i.e. a
/// list of tuples where Item1 is the list itself, and Item2 is the default value (to fill the list with). In
/// each tuple, the type T must be the same for the list and the default value, but between the tuples the type
/// might vary.
/// </summary>
/// <param name="listCollection">A collection of tuples with a List<T> and a default value T</param>
/// <returns>The length of the lists after the sync (length of the longest list before the sync)</returns>
public static int SyncListLength(IEnumerable<dynamic> listCollection)
{
int maxNumberOfItems = LengthOfLongestList(listCollection);
PadListsWithDefaultValue(listCollection, maxNumberOfItems);
return maxNumberOfItems;
}
private static int LengthOfLongestList(IEnumerable<dynamic> listCollection)
{
return listCollection.Aggregate(0, (current, tuple) => Math.Max(current, tuple.Item1.Count));
}
private static void PadListsWithDefaultValue(IEnumerable<dynamic> listCollection, int maxNumberOfItems)
{
foreach (dynamic tuple in listCollection)
{
FillList(tuple.Item1, tuple.Item2, maxNumberOfItems);
}
}
private static void FillList<T>(ICollection<T> list, T fillValue, int maxNumberOfItems)
{
int itemsToAdd = maxNumberOfItems - list.Count;
for (int i = 0; i < itemsToAdd; i++)
{
list.Add(fillValue);
}
}
}
}
以下是我用来验证是否获得了所需行为的简短单元测试集:
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Foo.utils;
namespace Foo.UnitTests
{
[TestClass]
public class DynamicListSync
{
private readonly List<string> stringList = new List<string>();
private readonly List<bool> boolList = new List<bool>();
private readonly List<string> stringListWithCustomDefault = new List<string>();
private readonly List<int> intList = new List<int>();
private readonly List<dynamic> listCollection = new List<dynamic>();
private const string FOO = "bar";
[TestInitialize]
public void InitTest()
{
listCollection.Add(Tuple.Create(stringList, default(String)));
listCollection.Add(Tuple.Create(boolList, default(Boolean)));
listCollection.Add(Tuple.Create(stringListWithCustomDefault, FOO));
listCollection.Add(Tuple.Create(intList, default(int)));
}
[TestMethod]
public void SyncEmptyLists()
{
Assert.AreEqual(0, ListCollectionHelper.SyncListLength(listCollection));
}
[TestMethod]
public void SyncWithOneListHavingOneItem()
{
stringList.Add("one");
Assert.AreEqual(1, ListCollectionHelper.SyncListLength(listCollection));
Assert.AreEqual("one", stringList[0]);
Assert.AreEqual(default(Boolean), boolList[0]);
Assert.AreEqual(FOO, stringListWithCustomDefault[0]);
Assert.AreEqual(default(int), intList[0]);
}
[TestMethod]
public void SyncWithAllListsHavingSomeItems()
{
stringList.Add("one");
stringList.Add("two");
stringList.Add("three");
boolList.Add(false);
boolList.Add(true);
stringListWithCustomDefault.Add("one");
Assert.AreEqual(3, ListCollectionHelper.SyncListLength(listCollection));
Assert.AreEqual("one", stringList[0]);
Assert.AreEqual("two", stringList[1]);
Assert.AreEqual("three", stringList[2]);
Assert.AreEqual(false, boolList[0]);
Assert.AreEqual(true, boolList[1]);
Assert.AreEqual(default(Boolean), boolList[2]);
Assert.AreEqual("one", stringListWithCustomDefault[0]);
Assert.AreEqual(FOO, stringListWithCustomDefault[1]);
Assert.AreEqual(FOO, stringListWithCustomDefault[2]);
Assert.AreEqual(default(int), intList[0]);
Assert.AreEqual(default(int), intList[1]);
Assert.AreEqual(default(int), intList[2]);
}
}
}
所以,由于这是我第一次尝试动态编程(无论是在C#还是其他任何地方...),我只想问一下我是否做得对。显然,代码按预期工作,但这是正确的方式吗?我是否忽略了任何明显的优化或陷阱等?