簡體   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