簡體   English   中英

c#修改List中的結構<T>

[英]c# modifying structs in a List<T>

簡短問題:如何修改List單個項目? (或者更確切地說,存儲在List中的struct成員?)

完整說明:

首先,使用下面的struct定義:

public struct itemInfo
{
    ...(Strings, Chars, boring)...
    public String nameStr;
    ...(you get the idea, nothing fancy)...
    public String subNum;   //BTW this is the element I'm trying to sort on
}

public struct slotInfo
{
    public Char catID;
    public String sortName;
    public Bitmap mainIcon;
    public IList<itemInfo> subItems;
}

public struct catInfo
{
    public Char catID;
    public String catDesc;
    public IList<slotInfo> items;
    public int numItems;
}

catInfo[] gAllCats = new catInfo[31];

gAllCats在加載時填充,在程序運行時依此類推。

當我想在subItems數組中對itemInfo對象進行排序時,會出現問題。 我正在使用LINQ來執行此操作(因為似乎沒有任何其他合理的方法來排序非內置類型的列表)。 所以這就是我所擁有的:

foreach (slotInfo sInf in gAllCats[c].items)
{
    var sortedSubItems =
        from itemInfo iInf in sInf.subItems
        orderby iInf.subNum ascending
        select iInf;
    IList<itemInfo> sortedSubTemp = new List<itemInfo();
    foreach (itemInfo iInf in sortedSubItems)
    {
        sortedSubTemp.Add(iInf);
    }
    sInf.subItems.Clear();
    sInf.subItems = sortedSubTemp;   // ERROR: see below
}

錯誤是“無法修改'sInf'的成員,因為它是'foreach迭代變量'”。

a,這種限制毫無意義; 是不是foreach構造的主要用途?

b,(也是出於惡意)如果不修改列表,Clear()會做什么? (順便說一句,根據調試器,如果我刪除最后一行並運行它,List會被清除。)

所以我嘗試采用不同的方法,看看它是否使用常規for循環。 (顯然,這只是允許的,因為gAllCats[c].items實際上是一個IList ;我不認為它會允許你以這種方式索引常規List 。)

for (int s = 0; s < gAllCats[c].items.Count; s++)
{
    var sortedSubItems =
        from itemInfo iInf in gAllCats[c].items[s].subItems
        orderby iInf.subNum ascending
        select iInf;
    IList<itemInfo> sortedSubTemp = new List<itemInfo>();
    foreach (itemInfo iInf in sortedSubItems)
    {
        sortedSubTemp.Add(iInf);
    }
    //NOTE: the following two lines were incorrect in the original post
    gAllCats[c].items[s].subItems.Clear();
    gAllCats[c].items[s].subItems = sortedSubTemp;   // ERROR: see below
}

這一次,錯誤是“無法修改'System.Collections.Generic.IList.this [int]'的返回值,因為它不是變量。” 啊! 它是什么,如果不是變量? 什么時候它成為'回歸價值'?

我知道必須有一個'正確'的方法來做到這一點; 我是從C背景來看這個,我知道我可以在C中做到這一點(盡管有一些手動內存管理。)

我四處搜索,似乎ArrayList已經過時了,支持泛型類型(我使用的是3.0),我不能使用數組,因為大小需要是動態的。

查看for循環方法, 在編譯錯誤文檔中給出了原因(和解決方案):

嘗試修改由中間表達式生成但未存儲在變量中的值類型。 當您嘗試直接修改泛型集合中的結構時,可能會發生此錯誤。

要修改結構,首先將其分配給局部變量,修改變量,然后將變量分配回集合中的項目。

因此,在for循環中,更改以下行:

catSlots[s].subItems.Clear();
catSlots[s].subItems = sortedSubTemp;   // ERROR: see below

...到:

slotInfo tempSlot = gAllCats[0].items[s];
tempSlot.subItems  = sortedSubTemp;
gAllCats[0].items[s] = tempSlot;

我刪除了對Clear方法的調用,因為我認為它沒有添加任何內容。

你在foreach中遇到的問題是結構是值類型,因此,循環迭代變量實際上不是對列表中結構的引用,而是結構的副本。

我的猜測是編譯器禁止你改變它,因為它很可能不會做你想象的那樣。

subItems.Clear()不是問題,因為雖然該字段可能是列表中元素的副本,但它也是對列表的引用(淺層副本)。

最簡單的解決方案可能是從struct變為class 或者使用與for (int ix = 0; ix < ...; ix++)等完全不同的方法。

foreach循環不起作用,因為sInf是項內部結構的副本。 更改sInf不會更改列表中的“實際”結構。

清除有效,因為您沒有更改sInf,您正在更改sInf中的列表,而Ilist<T>將始終是引用類型。

當您在IList<T>上使用索引運算符時會發生同樣的事情 - 它返回一個副本而不是實際的結構。 如果編譯器確實允許catSlots[s].subItems = sortedSubTemp; ,您將修改副本的子項,而不是實際的結構。 現在你明白為什么編譯器說返回值不是變量 - 副本不能再被引用。

有一個相當簡單的修復 - 對副本進行操作,然后用您的副本覆蓋原始結構。

for (int s = 0; s < gAllCats[c].items.Count; s++)
{
            var sortedSubItems =
                            from itemInfo iInf in gAllCats[c].items[s].subItems
                            orderby iInf.subNum ascending
                            select iInf;
            IList<itemInfo> sortedSubTemp = new List<itemInfo>();
            foreach (itemInfo iInf in sortedSubItems)
            {
                            sortedSubTemp.Add(iInf);
            }
            var temp = catSlots[s];
            temp.subItems = sortedSubTemp;
            catSlots[s] = temp;
}

是的,這導致兩個復制操作,但這是您為值語義支付的價格。

您指定的兩個錯誤與您使用結構的事實有關,結構在C#中是值類型,而不是引用類型。

你絕對可以在foreach循環中使用引用類型。 如果將結構更改為類,則可以執行以下操作:

    foreach(var item in gAllCats[c].items)
    {
        item.subItems = item.subItems.OrderBy(x => x.subNum).ToList();
    }

對於結構,這需要更改為:

    for(int i=0; i< gAllCats[c].items.Count; i++)
    {
        var newitem = gAllCats[c].items[i];
        newitem.subItems = newitem.subItems.OrderBy(x => x.subNum).ToList();
        gAllCats[c].items[i] = newitem;
    }

其他答案有更好的信息,為什么結構不同於類,但我認為我可以幫助排序部分。

如果subItems更改為具體的List而不是IList接口,那么您將能夠使用Sort方法。

public List<itemInfo> subItems;

所以你的整個循環變成:

foreach (slotInfo sInf in gAllCats[c].items)
    sInf.subItems.Sort();

這根本不需要修改struct的內容(通常是一件好事)。 struct的成員仍將指向完全相同的對象。

此外,在C#中使用struct原因很少。 GC是非常非常好的,除非你在分析器中證明了內存分配瓶頸,否則你最好還是class

更簡潔地說,如果itemsgAllCats[c].items也是一個List ,你可以這樣寫:

gAllCats[c].items.ForEach(i => i.subItems.Sort());

編輯:你太容易放棄了! :)

Sort很容易定制。 例如:

var simpsons = new[]
               {
                   new {Name = "Homer", Age = 37},
                   new {Name = "Bart", Age = 10},
                   new {Name = "Marge", Age = 36},
                   new {Name = "Grandpa", Age = int.MaxValue},
                   new {Name = "Lisa", Age = 8}
               }
               .ToList();

simpsons.Sort((a, b) => a.Age - b.Age);

從最小到最古老的那種。 (C#3中的類型推斷不是很好嗎?)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM