如何将项添加到通用列表?

时间:2015-03-23 13:50:26

标签: c# generics visual-studio-2013

让我解释一下我想要完成的事情。我有大约十二个不同的对象列表和十二种不同的方法来根据它们包含的内容填充它们。我一直试图重新考虑代码以消除“湿”部分,并将其降低到不可减少的最小值。

十二个班级中的每一个都非常相似。想想他们是这样的。一些具有更多属性,一些具有更少,并且具有各种类型。

class car
{
    public string brand_model { get; set; }
    public int numberOfDoors { get; set; }
    public string engineType { get; set; }
}

class bike
{
    public string brand_model { get; set;}
    public int numberOfMainCogs { get; set;}
    public int weight { get; set;}
}

我有三种方法。 top方法传递给下一个方法,无论它当前使用哪个列表作为通用:

List<car> carList = new List<car>();
List<bike> bikeList = new List<bike>();

switch (vehicleType)
{
    case ("car"):
        Populate(ref carList);
        break;
    case ("bike"):
        Populate(ref bikeList);
        break;
 }

我调用populate,它只是将该空列表发送到从数据库中读取的方法。

public bool Populate<T>(ref List<T> vehicleList)
{
//figure out what kind of list was passed in
//do some stuff to connect to database
//call appropriate method to add items to that list
switch(vehicleType)
{
    case("car"):
        readCarInformation(reader, ref vehicleList);
        break;
    case("bike"):
        readBikeInformation(reader, ref vehicleList);
        break;
}

让我们说这是一辆汽车。这是我的方法声明。

private void readCarInformation(AseDataReader reader, ref List<car> carList)
    {
        while (reader.Read())
        {
            carList.Add(new car
            {
                brand_model = SafeGetInt(reader, 0),
                numberOfDoors = SafeGetInt(reader, 1),
                engineType = SafeGetString(reader, 2)
            });
        }
    }
}

我现在已尝试过多种方法来实现这一目标。我正在尝试填充我在原始方法中制作的carList。我做错了什么?

是的,我还是个新手。

2 个答案:

答案 0 :(得分:4)

通常,当您必须使用反射来确定泛型参数的“类型”时,根据该类型执行不同的操作,您不应该使用泛型。更好的方法是为每种类型设置重载(将依赖于反射的任何公共代码分解):

public bool Populate(ref List<car> carList)
{
    //do some stuff to connect to database
    AseDataReader reader = GetReader();

    readCarInformation(reader, ref carList);
}        
public bool Populate(ref List<bike> bikeList)
{
    //do some stuff to connect to database
    AseDataReader reader = GetReader();

    readBikeInformation(reader, ref bikeList);
}

代码量大致相同(交换多个方法的switch开销),编译时安全,并且提高了可测试性(您可以单独测试每个Populate方法比多个案例测试一个Populate方法。

我还会质疑ref是否必要。如果您只是添加到传入的列表实例,则不需要ref。如果您要分配列表,那么out可能更合适(或使用List返回类型而不是布尔值。)

答案 1 :(得分:1)

  • 您不需要对列表使用ref,因为作为参数传递时不会复制对象。
  • 我认为你的readXXInformation应该只读取信息,把信息放在列表中太专业了,应该在其他地方执行。认为您可能希望将其用于其他目的。
  • 最后,您不需要执行多个开关。

这是一种方法:

List<car> carList = new List<car>();
List<bike> bikeList = new List<bike>();

switch (vehicleType)
{
    case ("car"):
        carList.AddRange(readCarInformation(reader));
        break;
    case ("bike"):
        bikeList.AddRange(readBikeInformation(reader));
        break;
}

你的读者会是这样的:

private IEnumerable<Car> readCarInformation(AseDataReader reader)
{
    while (reader.Read())
    {
        yield return new car
        {
            brand_model = SafeGetInt(reader, 0),
            numberOfDoors = SafeGetInt(reader, 1),
            engineType = SafeGetString(reader, 2)
        };
    }
}

是的,我删除了您的Populate功能。如果你真的需要做更多只是将元素添加到列表中的东西,你可以使用这样的函数:

private void Populate<T>(IList<T> destination, IEnumerable<T> source)
{
    destination.AddRange(source);
    ... // others stuffs.
}

你的开关将是:

switch (vehicleType)
{
    case ("car"):
        Populate(carList, readCarInformation(reader));
        break;
    case ("bike"):
        Populate(bikeList, readBikeInformation(reader));
        break;
}

<强> [编辑]

你也可以像这样分解readXXInformation

private IEnumerable<T> readObjectInformation<T>(AseDataReader reader, Func<AseDataReader, T> objectBuilder)
{
    while (reader.Read())
    {
        yield return objectBuilder(reader);
    }
}

并使用像这样的小对象构建器:

private Car carBuilder(AseDataReader reader)
{
    return new car
        {
            brand_model = SafeGetInt(reader, 0),
            numberOfDoors = SafeGetInt(reader, 1),
            engineType = SafeGetString(reader, 2)
        };
}

最后你像这样使用它:

switch (vehicleType)
{
    case ("car"):
        carList.AddRange(readObjectInformation(reader, carBuilder));
        break;
    case ("bike"):
        bikeList.AddRange(readObjectInformation(reader, bikeBuilder));
        break;
}

private void Populate<T>(AseDataReader reader, IList<T> destination, Func<AseDataReader, T> objectBuilder)
{
    destination.AddRange(readObjectInformation(reader, objectBuilder));
    ... // others stuffs.
}

...

switch (vehicleType)
{
    case ("car"):
        Populate(reader, carList, carBuilder);
        break;
    case ("bike"):
        Populate(reader, bikeList, bikeBuilder);
        break;
}

有很多解决方案。