繁体   English   中英

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

[英]How to add an item to a generic list?

让我解释一下我要完成的工作。 我大约有十二种不同的对象列表和十二种不同的方法,以根据它们包含的内容填充它们。 我一直在尝试对代码进行重构,以消除“湿”部分,并将其降至不可减少的最小值。

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

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

我有三种方法。 顶部方法传递给下一个方法,无论该方法当前正在使用哪个列表作为泛型:

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

我称之为填充,它只是将那个空列表发送到从数据库读取的方法。

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。 我做错了什么?

是的,我仍然是新手。

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

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返回类型而不是布尔值)。

  • 您不需要为列表使用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;
}

有很多解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM