එකතු කිරීම වෙනස් කරන ලදි; ගණන් ගැනීමේ මෙහෙයුම ක්‍රියාත්මක නොවිය හැක


939

මට මෙම දෝෂයේ පහළට යා නොහැක, මන්ද නිදොස්කරණය ඇමිණූ විට එය සිදු වූ බවක් නොපෙනේ.

එකතු කිරීම වෙනස් කරන ලදි; ගණන් ගැනීමේ මෙහෙයුම ක්‍රියාත්මක නොවිය හැක

පහත දැක්වෙන්නේ කේතයයි.

මෙය වින්ඩෝස් සේවාවක WCF සේවාදායකයකි. NotifySubscribers()දත්ත සිදුවීමක් ඇති සෑම අවස්ථාවකම මෙම ක්‍රමය සේවාව විසින් කැඳවනු ලැබේ (අහඹු ලෙස, නමුත් බොහෝ විට නොවේ - දිනකට 800 වතාවක් පමණ).

වින්ඩෝස් පෝරම සේවාදායකයකු දායක වූ විට, ග්‍රාහක හැඳුනුම්පත ග්‍රාහක ශබ්දකෝෂයට එකතු වන අතර සේවාදායකයා දායකත්වයෙන් ඉවත් වූ විට එය ශබ්දකෝෂයෙන් මකා දමනු ලැබේ. සේවාදායකයා දායකත්වයෙන් ඉවත් වූ විට (හෝ පසුව) දෝෂය සිදු වේ. ඊළඟ වතාවේ NotifySubscribers()ක්‍රමය හැඳින්වූ foreach()විට විෂය රේඛාවේ දෝෂය සමඟ ලූපය අසමත් වන බව පෙනේ . පහත කේතයෙහි පෙන්වා ඇති පරිදි මෙම ක්‍රමය මඟින් දෝෂය යෙදුම් ලොගයට ලියයි. නිදොස් කිරීමක් අමුණා ඇති විට සහ සේවාදායකයා දායකත්වයෙන් ඉවත් වූ විට, කේතය හොඳින් ක්‍රියාත්මක වේ.

මෙම කේතයේ ගැටලුවක් ඔබට පෙනේද? මට ශබ්ද කෝෂය නූල් ආරක්ෂිත කිරීමට අවශ්‍යද?

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class SubscriptionServer : ISubscriptionServer
{
    private static IDictionary<Guid, Subscriber> subscribers;

    public SubscriptionServer()
    {            
        subscribers = new Dictionary<Guid, Subscriber>();
    }

    public void NotifySubscribers(DataRecord sr)
    {
        foreach(Subscriber s in subscribers.Values)
        {
            try
            {
                s.Callback.SignalData(sr);
            }
            catch (Exception e)
            {
                DCS.WriteToApplicationLog(e.Message, 
                  System.Diagnostics.EventLogEntryType.Error);

                UnsubscribeEvent(s.ClientId);
            }
        }
    }
    
    public Guid SubscribeEvent(string clientDescription)
    {
        Subscriber subscriber = new Subscriber();
        subscriber.Callback = OperationContext.Current.
                GetCallbackChannel<IDCSCallback>();

        subscribers.Add(subscriber.ClientId, subscriber);
        
        return subscriber.ClientId;
    }

    public void UnsubscribeEvent(Guid clientId)
    {
        try
        {
            subscribers.Remove(clientId);
        }
        catch(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("Unsubscribe Error " + 
                    e.Message);
        }
    }
}

මගේ නඩුවේදී එය සම පාර්ශවීය බලපෑමක් විය. මන්දයත් මම කිහිපයක් භාවිතා කරන බැවිනි .එය ක්‍රියාවලියේදී වෙනස් කරන ලද ("වගුව" ඇතුළත් කරන්න - කේතය කියවීමේදී එතරම් පැහැදිලි නැත. කෙසේ වෙතත් මම ඒ අවශ්ය විය නොහැකි (! ඔව් පැරණි, unmaintained කේතය) මම ද ඔවුන් පමණක් ඉවත් කිරීමෙන් මගේ ප්රශ්නය නිරාකරණය ඇතුලත් වේ වාසනාවන්ත විය
අඩි

කරුණාකර ලබා දුන් පිළිතුර දෙස බලන්න. එය බොහෝ අවස්ථාවල වඩා හොඳ විසඳුමකි. stackoverflow.com/a/57799537/10307728
තැඹිලි කැටපිලර්

Answers:


1675

සිදුවිය හැකි දෙය SignalDataනම්, ලූපය තුළදී ග්‍රාහක ශබ්ද කෝෂය වක්‍රව වෙනස් කිරීම සහ එම පණිවිඩයට මඟ පෙන්වීමයි. වෙනස් කිරීමෙන් ඔබට මෙය සත්‍යාපනය කළ හැකිය

foreach(Subscriber s in subscribers.Values)

වෙත

foreach(Subscriber s in subscribers.Values.ToList())

මම හරි නම්, ගැටලුව අතුරුදහන් වනු ඇත.

ඇමතීම ආරම්භයේ subscribers.Values.ToList()දී අගයන් subscribers.Valuesවෙනම ලැයිස්තුවකට පිටපත් කරයි foreach. වෙන කිසිම දෙයකට මෙම ලැයිස්තුවට ප්‍රවේශය නැත (එයට විචල්‍ය නමක්වත් නැත!), එබැවින් ලූපය තුළ එය වෙනස් කළ නොහැක.


14
.NET 2.0 යෙදුම් සමඟ නොගැලපෙන System.Core dll හි BTW .ToList () පවතී. එබැවින් ඔබේ ඉලක්ක යෙදුම .Net 3.5
mishal153

61
ඔබ ටෝලිස්ට් එකක් කළේ ඇයි සහ එය සියල්ල නිවැරදි කරන්නේ ඇයිදැයි මට තේරෙන්නේ නැත
ධනාත්මක

207
OfCoffeeAddict: ගැටළුව වන්නේ ලූපය subscribers.Valuesතුළ වෙනස් කිරීමයි foreach. ඇමතීම ආරම්භයේ subscribers.Values.ToList()දී අගයන් subscribers.Valuesවෙනම ලැයිස්තුවකට පිටපත් කරයි foreach. වෙන කිසිම දෙයකට මෙම ලැයිස්තුවට ප්‍රවේශය නැත (එයට විචල්‍ය නමක් පවා නැත!) , එබැවින් කිසිවක් ලූපය තුළ වෙනස් කළ නොහැක.
බ්ලූරාජා - ඩැනී ප්ලග්ගොෆ්ට්

35
ක්‍රියාත්මක කිරීමේදී ToListඑකතු කිරීම වෙනස් කර ඇත්නම් එය විසි කළ හැකි බව සලකන්න ToList.
ශ්‍රීරාම් ශක්තිවෙල්

20
මට විශ්වාසයි, ගැටලුව විසඳන්නේ නැති බව, නමුත් හුදෙක් ප්‍රතිනිෂ්පාදනය කිරීම අපහසු කරයි. ToListපරමාණුක මෙහෙයුමක් නොවේ. ඊටත් වඩා විනෝදජනක දෙය නම්, ToListමූලික foreachවශයෙන් අයිතමයන් නව ලැයිස්තු උදාහරණයකට පිටපත් කිරීම සඳහා අභ්‍යන්තරව එයම කරයි , එයින් අදහස් කරන්නේ ඔබ foreachඅතිරේක (ඉක්මන් වුවද) foreachපුනරාවර්තනයක් එකතු කිරීමෙන් ගැටළුවක් නිරාකරණය කර ඇති බවයි .
Groo

115

ග්‍රාහකයෙකු දායකත්වයෙන් ඉවත් වූ විට ඔබ ගණන් ගැනීමේදී ග්‍රාහකයින්ගේ එකතුවෙහි අන්තර්ගතය වෙනස් කරයි.

මෙය නිවැරදි කිරීමට ක්‍රම කිහිපයක් තිබේ, එකක් පැහැදිලිව භාවිතා කිරීම සඳහා ලූපය වෙනස් කිරීම .ToList():

public void NotifySubscribers(DataRecord sr)  
{
    foreach(Subscriber s in subscribers.Values.ToList())
    {
                                              ^^^^^^^^^  
        ...

හොඳයි ටොලිස්ට් () පරමාණුක නොවේ. එය තවමත් විසි කළ හැකිය, නමුත් ටොලිස්ට් () වෙතින්!
joe

කෙසේද? OP අසයි "එකතුව වෙනස් කරන ලදි". පිටපතක් සෑදීම එය විසඳිය හැකි ක්‍රමයක් වන නමුත් ආරක්ෂිත ආකාරයකින් මිස. ToList මඟින් දෝෂය ToList () වෙත මාරු කරයි. එය විසඳීම වෙනුවට ඔබට තවත් දෝෂයක් ලැබෙනු ඇත : destination array was not long enough. dotnetfiddle.net/cFUdca
joe

66

වඩාත් කාර්යක්ෂම ක්‍රමයක් නම්, මගේ මතය අනුව, ඔබ ඉවත් කළ යුතු ඕනෑම දෙයක් දැමූ බව ඔබ ප්‍රකාශ කරන තවත් ලැයිස්තුවක් තිබීමයි. ඔබේ ප්‍රධාන ලූපය (.ToList () නොමැතිව) අවසන් කිරීමෙන් පසුව, ඔබ "ඉවත් කළ යුතු" ලැයිස්තුවට ඉහළින් තවත් ලූපයක් සිදු කරයි. එබැවින් ඔබේ පන්තියේදී ඔබ එකතු කරන්නේ:

private List<Guid> toBeRemoved = new List<Guid>();

ඉන්පසු ඔබ එය වෙනස් කරන්නේ:

public void NotifySubscribers(DataRecord sr)
{
    toBeRemoved.Clear();

    ...your unchanged code skipped...

   foreach ( Guid clientId in toBeRemoved )
   {
        try
        {
            subscribers.Remove(clientId);
        }
        catch(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("Unsubscribe Error " + 
                e.Message);
        }
   }
}

...your unchanged code skipped...

public void UnsubscribeEvent(Guid clientId)
{
    toBeRemoved.Add( clientId );
}

මෙය ඔබගේ ගැටළුව විසඳනවා පමණක් නොව, ඔබේ ශබ්ද කෝෂයෙන් ලැයිස්තුවක් සෑදීම වළක්වාලනු ඇත, එහි විශාල ග්‍රාහකයින් සිටී නම් එය මිල අධිකය. කිසියම් පුනරාවර්තනයක් මත ඉවත් කළ යුතු ග්‍රාහකයින්ගේ ලැයිස්තුව ලැයිස්තුවේ ඇති මුළු සංඛ්‍යාවට වඩා අඩු යැයි උපකල්පනය කිරීම, මෙය වේගවත් විය යුතුය. ඔබේ නිශ්චිත භාවිත තත්වය තුළ කිසියම් සැකයක් ඇත්නම් එය එසේ බව සහතික කර ගැනීමට ඇත්ත වශයෙන්ම එය පැතිකඩ කිරීමට නිදහස් වන්න.


9
ඔබ විශාල එකතු කිරීම් සමඟ වැඩ කරන්නේ නම් මෙය සලකා බැලීම වටී යැයි මම බලාපොරොත්තු වෙමි. එය කුඩා නම් මම බොහෝ විට ලැයිස්තු ගත කර ඉදිරියට යන්නෙමි.
කාල් කීනින්ගර්

44

ඔබේ ග්‍රාහකයින්ගේ ශබ්දකෝෂය එය ලිහිල් වන සෑම විටම වෙනස් කිරීම වැළැක්වීම සඳහා ඔබට එය අගුළු දැමිය හැකිය:

 lock (subscribers)
 {
         foreach (var subscriber in subscribers)
         {
               //do something
         }
 }

2
එය සම්පූර්ණ උදාහරණයක් ද? මාකර් ෆ්‍රීක්වෙන්ෂන්ස් නම් වූ සාමාන්‍ය ශබ්දකෝෂයක් <string, int> අඩංගු පන්තියක් (_dictionary obj) ඇත, නමුත් මෙය කිරීමෙන් ක්ෂණිකව කඩාවැටීම නිරාකරණය නොවීය: අගුල (_dictionary.MarkerFrequencies) {foreach (KeyValuePair <string, int> pair in pair _dictionary.MarkerFrequencies) {...}}
ජෝන් කුම්බ්ස්

4
@JCoombs ඔබ නවීකරණය කිරීමට ඉඩ ඇත, සමහර විට MarkerFrequenciesශබ්දකෝෂය අගුල තුළම නැවත පැවරිය හැකිය, එයින් අදහස් වන්නේ මුල් අවස්ථාව තවදුරටත් අගුළු දමා නොමැති බවයි. A forවෙනුවට ආදේශකයක් භාවිතා කිරීමට උත්සාහ කරන්න foreach, මෙය සහ මෙය බලන්න . එය විසඳන්නේ දැයි මට දන්වන්න.
මොහොමඩ් සේපාවාන්ඩ්

1
හොඳයි, මම අවසානයේ මෙම දෝෂය නිරාකරණය කර ගත්තා, මෙම ඉඟි ප්‍රයෝජනවත් විය - ස්තූතියි! මගේ දත්ත කට්ටලය කුඩා වූ අතර, මෙම ක්‍රියාව UI දර්ශන යාවත්කාලීන කිරීමක් පමණක් වන අතර, එම නිසා පිටපතක් නැවත යෙදීම වඩාත් සුදුසු බව පෙනේ. (මා නූල් දෙකෙහිම මාකර් සංඛ්‍යාත අගුළු දැමීමෙන් පසු පුරෝකථනය වෙනුවට භාවිතා කිරීම පිළිබඳව ද අත්හදා බැලුවෙමි. මෙය බිඳවැටීම වළක්වා ඇති නමුත් තවදුරටත් නිදොස් කිරීමේ වැඩක් අවශ්‍ය බව පෙනෙන්නට තිබුණි. තවද එය වඩාත් සංකීර්ණ බවක් හඳුන්වා දුන්නේය. මෙහි නූල් දෙකක් තිබීමට මගේ එකම හේතුව පරිශීලකයා අවලංගු කිරීමට ඉඩ දීමයි. යූඅයි විසින් එම දත්ත කෙලින්ම වෙනස් නොකරයි.)
ජෝන් කුම්බ්ස්

11
ගැටළුව නම්, විශාල යෙදුම් සඳහා, අගුල් ප්‍රධාන කාර්ය සාධනයක් විය හැකිය - System.Collections.Concurrentනාම අවකාශයේ එකතුවක් භාවිතා කිරීම වඩා හොඳය .
BrainSlugs83

32

මෙම දෝෂය ඇයි?

පොදුවේ .නෙට් එකතු කිරීම් එකවර ගණනය කිරීම හා වෙනස් කිරීම සඳහා සහාය නොදක්වයි. ගණන් ගැනීමේදී එකතු කිරීමේ ලැයිස්තුව වෙනස් කිරීමට ඔබ උත්සාහ කරන්නේ නම්, එය ව්‍යතිරේකයක් මතු කරයි. එබැවින් මෙම දෝෂය පිටුපස ඇති ගැටළුව නම්, අප විසින් ලිහිල් කරන අතරතුර ලැයිස්තුව / ශබ්ද කෝෂය වෙනස් කළ නොහැක.

විසඳුම් වලින් එකක්

අපි එහි ශබ්ද ලැයිස්තුවක් භාවිතා කරමින් ශබ්ද කෝෂයක් පුනරාවර්තනය කරන්නේ නම්, සමාන්තරව අපට ශබ්දකෝෂ වස්තුව වෙනස් කළ හැකිය, මන්ද යත් අපි යතුරු එකතු කිරීම හරහා ශබ්දකෝෂය හරහා නොව (සහ එහි යතුරු එකතුව නැවත සැකසීම) ය.

උදාහරණයක්

//get key collection from dictionary into a list to loop through
List<int> keys = new List<int>(Dictionary.Keys);

// iterating key collection using a simple for-each loop
foreach (int key in keys)
{
  // Now we can perform any modification with values of the dictionary.
  Dictionary[key] = Dictionary[key] - 1;
}

මෙන්න මෙම විසඳුම පිළිබඳ බ්ලොග් සටහනක් .

StackOverflow හි ගැඹුරු කිමිදීම සඳහා: මෙම දෝෂය සිදුවන්නේ ඇයි?


ඔබට ස්තුතියි! එය සිදුවන්නේ ඇයිද යන්න පිළිබඳ පැහැදිලි පැහැදිලි කිරීමක් සහ මගේ අයදුම්පතේ මෙය විසඳීමට වහාම මට මග ලබා දුන්නේය.
කිම් ක්‍රොසර්

සී # 8.0 සමඟ .නෙට් කෝර් 3.0 අනුව, ශබ්ද කෝෂයක් ගණනය කිරීමේදී (පුරෝකථනය කිරීමේදී) වෙනස් කළ හැකිය . ඉවත් කරන්න සහ ඉවත් කරන්න . මෙය වෙනත් එකතු කිරීම් වලට අදාළ නොවේ.
සුපර් ජේඩ්

5

ඇත්ත වශයෙන්ම ගැටලුව මට පෙනෙන්නේ ඔබ ලැයිස්තුවෙන් අංග ඉවත් කර කිසිවක් සිදු නොවූ ලෙස ලැයිස්තුව දිගටම කියවීමට බලාපොරොත්තු වන බවයි.

ඔබ සැබවින්ම කළ යුත්තේ අවසානය සිට නැවත ආරම්භය දක්වා ආරම්භ කිරීමයි. ඔබ ලැයිස්තුවෙන් අංග ඉවත් කළත් ඔබට එය දිගටම කියවිය හැකිය.


එය කිසියම් වෙනසක් ඇති කරන්නේ කෙසේදැයි මට නොපෙනේද? ඔබ ලැයිස්තුවේ මැද ඇති මූලද්‍රව්‍යයක් ඉවත් කළ හොත් එය ප්‍රවේශ වීමට උත්සාහ කරන අයිතමය සොයාගත නොහැකි බැවින් එය තවමත් දෝෂයක් ඇති කරයි ද?
සැප්නොලොජිකා

4
Ap සැප්නොලොජිකා වෙනස නම් - ඔබ ලැයිස්තුව ගණනය නොකරනු ඇත - එක් එක් / එක් කිරීම වෙනුවට, ඔබ කරන්නේ / ඊළඟට කිරීම සහ පූර්ණ සංඛ්‍යා මඟින් එය ප්‍රවේශ කිරීම - ඔබට අනිවාර්යයෙන්ම ලැයිස්තුවක් a හි වෙනස් කළ හැකිය. / ඊළඟ ලූපය සඳහා, නමුත් කිසි විටෙකත් / එක් එක් ලූපය සඳහා (මන්ද / එක් එක් ගණනය කිරීම් සඳහා ) - ඔබට ඔබේ කවුන්ටර සකස් කිරීමට අමතර තර්කනයක් තිබේ නම්, ඔබට / ඊළඟට ඉදිරියට යා හැකිය.
BrainSlugs83

5

හරි ඉතින් මට උදව් වුණේ පසුපසට යෑමයි. මම ලැයිස්තුවකින් ප්‍රවේශයක් ඉවත් කිරීමට උත්සාහ කළ නමුත් එය නැවත ඉහළට යමින් එය තවදුරටත් ලූපය ඉස්කුරුප්පු කළේය.

for (int x = myList.Count - 1; x > -1; x--)
{
    myList.RemoveAt(x);
}

4

අවලංගු මෙහෙයුම් ක්‍රියාකාරීත්වය- ක්‍රියාදාමයක් සිදුවී ඇත. එය පුරෝකථනය කරන ලූපයක “එකතුවක් වෙනස් කරන ලදි” යනුවෙන් වාර්තා කරයි

වස්තුව ඉවත් කළ පසු, බිඳීමේ ප්‍රකාශය භාවිතා කරන්න.

උදා:

ArrayList list = new ArrayList(); 

foreach (var item in list)
{
    if(condition)
    {
        list.remove(item);
        break;
    }
}

1
ඔබේ ලැයිස්තුවේ එක් අයිතමයක්වත් ඉවත් කළ යුතු බව ඔබ දන්නේ නම් හොඳ සහ සරල විසඳුමක්.
තවාබ් වකිල්

3

පිළිගත් පිළිතුර නරකම අවස්ථාවෙහි නිරවද්‍ය හා වැරදි ය. කාලය තුළ වෙනස්කම් සිදු කර ඇත්නම්ToList() , ඔබට තවමත් දෝෂයක් ඇතිවිය හැකිය. lockඔබ මහජන සාමාජිකයෙකු සිටී නම්, කුමන කාර්ය සාධනය සහ නූල්-ආරක්ෂාව සැලකිල්ලට ගත යුතුද යන්නට අමතරව , නිසි විසඳුමක් වෙනස් කළ නොහැකි වර්ග භාවිතා කළ හැකිය .

පොදුවේ ගත් කල, වෙනස් කළ නොහැකි වර්ගයක් යනු වරක් නිර්මාණය කළ පසු එහි තත්වය ඔබට වෙනස් කළ නොහැකි බවයි. එබැවින් ඔබේ කේතය මෙසේ විය යුතුය:

public class SubscriptionServer : ISubscriptionServer
{
    private static ImmutableDictionary<Guid, Subscriber> subscribers = ImmutableDictionary<Guid, Subscriber>.Empty;
    public void SubscribeEvent(string id)
    {
        subscribers = subscribers.Add(Guid.NewGuid(), new Subscriber());
    }
    public void NotifyEvent()
    {
        foreach(var sub in subscribers.Values)
        {
            //.....This is always safe
        }
    }
    //.........
}

ඔබට මහජන සාමාජිකයෙකු සිටී නම් මෙය විශේෂයෙන් ප්‍රයෝජනවත් වේ. foreachඑකතු කිරීම වෙනස් කිරීම ගැන කරදර නොවී වෙනත් පංතිවලට සෑම විටම වෙනස් කළ නොහැකි වර්ගවල සිටිය හැකිය .


2

මට එකම ගැටළුව ඇති අතර, forඒ වෙනුවට මම ලූපයක් භාවිතා කරන විට එය විසඳුණි foreach.

// foreach (var item in itemsToBeLast)
for (int i = 0; i < itemsToBeLast.Count; i++)
{
    var matchingItem = itemsToBeLast.FirstOrDefault(item => item.Detach);

   if (matchingItem != null)
   {
      itemsToBeLast.Remove(matchingItem);
      continue;
   }
   allItems.Add(itemsToBeLast[i]);// (attachDetachItem);
}

11
මෙම කේතය වැරදියි, කිසියම් මූලද්‍රව්‍යයක් ඉවත් කරන්නේ නම් එකතුවේ ඇති සමහර අයිතම මඟ හරිනු ඇත. උදාහරණයක් ලෙස: ඔබට var arr = ["a", "b", "c"] ඇති අතර පළමු පුනරාවර්තනයේදී (i = 0) ඔබ 0 වන ස්ථානයේ මූලද්‍රව්‍යය ඉවත් කරයි (මූලද්‍රව්‍යය "a"). මෙයින් පසු සියලු අරාව මූලද්‍රව්‍යයන් එක් ස්ථානයක් ඉහළට ගෙන යන අතර අරාව ["b", "c"] වනු ඇත. එබැවින්, ඊළඟ පුනරාවර්තනයේදී (i = 1) ඔබ 1 වන ස්ථානයේ මූලද්‍රව්‍යය පරීක්ෂා කරනු ඇති අතර එය "c" නොව "b" වේ. මේක වැරදියි. එය නිවැරදි කිරීම සඳහා, ඔබ පහළ සිට ඉහළට ගමන් කළ යුතුය
කැස්පාර් ඕසෝල්ස්

2

මම මේ සඳහා බොහෝ විකල්ප දැක ඇති නමුත් මට මෙය හොඳම එකයි.

ListItemCollection collection = new ListItemCollection();
        foreach (ListItem item in ListBox1.Items)
        {
            if (item.Selected)
                collection.Add(item);
        }

ඉන්පසු එකතු කිරීම හරහා සරලව ලූප කරන්න.

ListItemCollection හි අනුපිටපත් අඩංගු විය හැකි බව මතක තබා ගන්න. පෙරනිමියෙන් එකතුවට අනුපිටපත් එකතු වීම වලක්වනු නොලැබේ. අනුපිටපත් වලක්වා ගැනීම සඳහා ඔබට මෙය කළ හැකිය:

ListItemCollection collection = new ListItemCollection();
            foreach (ListItem item in ListBox1.Items)
            {
                if (item.Selected && !collection.Contains(item))
                    collection.Add(item);
            }

මෙම කේතය දත්ත සමුදායට අනුපිටපත් ඇතුල් වීම වළක්වන්නේ කෙසේද? මම ඒ හා සමාන දෙයක් ලියා ඇති අතර, මම ලැයිස්තු කොටුවෙන් නව පරිශීලකයින් එකතු කළ විට, මම දැනටමත් ලැයිස්තුවේ ඇති තෝරාගත් එකක් අහම්බෙන් තබා ගත්තොත්, එය අනුපිටපත් ඇතුළත් කිරීමක් නිර්මාණය කරයි. ඔබට යෝජනා තිබේද ike මයික්?
ජේමි

1

විශේෂිත ප්‍රවේශයක් අවශ්‍ය වන විශේෂිත අවස්ථාවක් මෙන්න:

  1. එම Dictionary නිතර සිදු කිරීමත් ඇත.
  2. මෙම Dictionaryවන්නේ බොහෝ කලාතුරකින් වෙනස් කර ඇත.

මෙම තත්වය තුළ සෑම ගණනය කිරීමකටම පෙර Dictionary(හෝ Dictionary.Values) පිටපතක් සෑදීම තරමක් මිල අධික විය හැකිය. මෙම ගැටළුව විසඳීම පිළිබඳ මගේ අදහස නම්, එකම හැඹිලි පිටපත බහු ගණන් කිරීම්වල නැවත භාවිතා කිරීම සහ ව්‍යතිරේක සඳහා IEnumeratorමුල් පිටපතක් Dictionaryනැරඹීමයි. පිටපත් කළ දත්ත සමඟ ගණන් ගන්නා තැනැත්තා හැඹිලිගත කර නව ගණන් කිරීමක් ආරම්භ කිරීමට පෙර ප්‍රශ්න කරනු ලැබේ. ව්‍යතිරේකයකදී හැඹිලි පිටපත ඉවතලනු ඇති අතර නව එකක් සාදනු ලැබේ. මෙන්න මම මෙම අදහස ක්‍රියාත්මක කිරීම:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

public class EnumerableSnapshot<T> : IEnumerable<T>, IDisposable
{
    private IEnumerable<T> _source;
    private IEnumerator<T> _enumerator;
    private ReadOnlyCollection<T> _cached;

    public EnumerableSnapshot(IEnumerable<T> source)
    {
        _source = source ?? throw new ArgumentNullException(nameof(source));
    }

    public IEnumerator<T> GetEnumerator()
    {
        if (_source == null) throw new ObjectDisposedException(this.GetType().Name);
        if (_enumerator == null)
        {
            _enumerator = _source.GetEnumerator();
            _cached = new ReadOnlyCollection<T>(_source.ToArray());
        }
        else
        {
            var modified = false;
            if (_source is ICollection collection) // C# 7 syntax
            {
                modified = _cached.Count != collection.Count;
            }
            if (!modified)
            {
                try
                {
                    _enumerator.MoveNext();
                }
                catch (InvalidOperationException)
                {
                    modified = true;
                }
            }
            if (modified)
            {
                _enumerator.Dispose();
                _enumerator = _source.GetEnumerator();
                _cached = new ReadOnlyCollection<T>(_source.ToArray());
            }
        }
        return _cached.GetEnumerator();
    }

    public void Dispose()
    {
        _enumerator?.Dispose();
        _enumerator = null;
        _cached = null;
        _source = null;
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public static class EnumerableSnapshotExtensions
{
    public static EnumerableSnapshot<T> ToEnumerableSnapshot<T>(
        this IEnumerable<T> source) => new EnumerableSnapshot<T>(source);
}

භාවිත උදාහරණය:

private static IDictionary<Guid, Subscriber> _subscribers;
private static EnumerableSnapshot<Subscriber> _subscribersSnapshot;

//...(in the constructor)
_subscribers = new Dictionary<Guid, Subscriber>();
_subscribersSnapshot = _subscribers.Values.ToEnumerableSnapshot();

// ...(elsewere)
foreach (var subscriber in _subscribersSnapshot)
{
    //...
}

අවාසනාවකට මෙන් මෙම අදහස පන්තිය සමග දැනට භාවිතා කළ නොහැක Dictionaryමන්ද, .NET Core 3.0 දී මෙම පන්තිය විසි කරන්නේ නැත වූ එකතුව වෙනස් කරන ලද්දේ හැර ගණනය කිරීමත් විට හා ක්රම Removeහා Clearආයාචනා කර ඇත. මා පරීක්ෂා කළ අනෙක් සියලුම බහාලුම් නිරන්තරයෙන් හැසිරේ. මම ක්රමානුකූලව මෙම පන්ති පරීක්ෂා: List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V>හා SortedDictionary<T,V>. Dictionary.NET Core හි පන්තියේ ඉහත සඳහන් ක්‍රම දෙක පමණක් ගණනය කිරීම අවලංගු නොකරයි.


යාවත්කාලීන කිරීම: හැඹිලි වල දිග සහ මුල් එකතුව සංසන්දනය කිරීමෙන් මම ඉහත ගැටළුව විසඳා ගතිමි. මෙම නිවැරදි කිරීම උපකල්පනය කරන්නේ ශබ්දකෝෂය සෘජුවම EnumerableSnapshotඉදිකිරීම්කරුගේ තර්කයක් ලෙස සම්මත වන අතර එහි අනන්‍යතාවය (උදාහරණයක් ලෙස) වැනි ප්‍රක්ෂේපණයකින් සඟවනු නොලැබේ : dictionary.Select(e => e).ΤοEnumerableSnapshot().


වැදගත්: ඉහත පන්තිය නූල් ආරක්ෂිත නොවේ . එය තනි ත්‍රෙඩ් එකකින් පමණක් ධාවනය වන කේත වලින් භාවිතා කිරීමට අදහස් කෙරේ.


0

ඔබට ග්‍රාහකයින්ගේ ශබ්ද කෝෂ වස්තුව එකම ආකාරයේ තාවකාලික ශබ්ද කෝෂ වස්තුවකට පිටපත් කර තාවකාලික ශබ්ද කෝෂ වස්තුව foreach loop භාවිතා කර නැවත කළ හැකිය.


(මෙම ලිපිය ප්‍රශ්නයට ගුණාත්මක පිළිතුරක් සපයන බවක් නොපෙනේ . කරුණාකර ඔබේ පිළිතුර සංස්කරණය කර ප්‍රශ්නයට අදහස් දැක්වීමක් ලෙස පළ කරන්න).
sɐunıɔ ןɐ qɐp

0

එබැවින් මෙම ගැටළුව විසඳීම සඳහා වෙනත් ක්‍රමයක් වනුයේ මූලද්‍රව්‍ය ඉවත් කිරීම වෙනුවට නව ශබ්ද කෝෂයක් නිර්මාණය කිරීම සහ ඔබට ඉවත් කිරීමට අවශ්‍ය නොවූ අංග පමණක් එකතු කර මුල් ශබ්ද කෝෂය නව එකක් සමඟ ප්‍රතිස්ථාපනය කිරීමයි. මෙය කාර්යක්ෂමතාව පිළිබඳ ගැටලුවක් යැයි මම නොසිතමි, මන්ද එය ඔබ ව්‍යුහය හරහා පුනරාවර්තනය කරන වාර ගණන වැඩි නොකරන බැවිනි.


0

එය ඉතා හොඳින් විස්තාරණය කළ එක් සබැඳියක් ඇති අතර විසඳුමක් ද ලබා දී ඇත. ඔබට නිසි විසඳුමක් තිබේ නම් එය උත්සාහ කරන්න කරුණාකර මෙහි පල කරන්න එවිට අනෙක් අයට තේරුම් ගත හැකිය. ලබා දී ඇති විසඳුම හරි නම් පෝස්ට් මෙන් වෙනත් අයට මෙම විසඳුම් උත්සාහ කළ හැකිය.

ඔබ මුල් සබැඳිය යොමු කිරීම සඳහා: - https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/

අපි .නෙට් අනුක්‍රමිකකරණ පංති භාවිතා කරන විට එහි අර්ථ දැක්වීම තුළ ගණන් කළ හැකි වර්ගයක්, එනම් එකතුවක් ඇත, ඔබේ කේතනය බහු-නූල් තත්වයන් යටතේ පවතින තැනක “එකතු කිරීම වෙනස් කරන ලදි; ගණන් ගැනීමේ ක්‍රියාවලිය ක්‍රියාත්මක නොවනු ඇත” යනුවෙන් වලංගු නොවන මෙහෙයුම් එක්සෙප්ෂන් ඔබට පහසුවෙන් ලැබෙනු ඇත. මෙහි මූලික හේතුව නම්, අනුක්‍රමිකකරණ පංති ගණන් කිරීම හරහා එකතු කිරීම හරහා පුනරාවර්තනය වීමයි. එනිසා ගැටළුව වෙනස් වන්නේ එකතුවක් හරහා එය නැවත සැකසීමට උත්සාහ කිරීමෙනි.

පළමු විසඳුම, ලැයිස්තු වස්තුව වෙත මෙහෙයුම ක්‍රියාත්මක කළ හැක්කේ වරකට එක් නූල් එකකින් පමණක් බව සහතික කිරීම සඳහා අපට සමමුහුර්තකරණ විසඳුමක් ලෙස අගුල භාවිතා කළ හැකිය. නිසැකවම, ඔබට එම වස්තුවේ එකතුවක් අනුක්‍රමික කිරීමට අවශ්‍ය නම්, ඒ සෑම එකක් සඳහාම, අගුල යොදන බව ඔබට කාර්ය සාධන ද penalty ුවමක් ලැබෙනු ඇත.

හොඳයි, .නෙට් 4.0 මගින් බහු-නූල් අවස්ථා සමඟ කටයුතු කිරීම පහසු කරයි. මෙම අනුක්‍රමික එකතු කිරීමේ ක්ෂේත්‍ර ගැටලුව සඳහා, අපට දැනගන්නට ලැබුණේ අපට නූල්-ආරක්ෂිත සහ FIFO එකතුවක් වන සහ කේත අගුලෙන් තොර වන සමගාමී කියු (MSDN පරීක්ෂා කරන්න) පන්තියෙන් ප්‍රයෝජන ගත හැකි බවයි.

මෙම පංතිය භාවිතා කරමින්, ඔබේ කේතය සඳහා ඔබට වෙනස් කිරීමට අවශ්‍ය දෑ එකතු කිරීමේ වර්ගය එය සමඟ ප්‍රතිස්ථාපනය කරයි, සමගාමී පෝලිමේ අවසානයට මූලද්‍රව්‍යයක් එක් කිරීමට එන්කියු භාවිතා කරන්න, එම අගුළු කේතය ඉවත් කරන්න. නැතහොත්, ඔබ වැඩ කරමින් සිටින ස්ථානයට ලැයිස්තුව වැනි එකතු කිරීමේ දේවල් අවශ්‍ය නම්, ඔබේ ක්ෂේත්‍රවලට සමගාමී කියු අනුවර්තනය වීමට ඔබට තවත් කේත කිහිපයක් අවශ්‍ය වේ.

එකතු කිරීම පරමාණුකව නිෂ්කාශනය කිරීමට ඉඩ නොදෙන යටින් පවතින ඇල්ගොරිතම නිසා BTW, සමගාමී Que ට පැහැදිලි ක්‍රමයක් නොමැත. එබැවින් ඔබ එය තනිවම කළ යුතුය, වේගවත්ම ක්‍රමය වන්නේ ප්‍රතිස්ථාපනය සඳහා නව හිස් සමගාමී පෝලිමක් නැවත නිර්මාණය කිරීමයි.

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.