IEnumerable এর একাধিক অঙ্কের জন্য সতর্কতা হ্যান্ডলিং


358

আমার কোডে IEnumerable<>বেশ কয়েকবার ব্যবহার করা দরকার সুতরাং "সম্ভাব্য একাধিক সংখ্যাবৃদ্ধির" এর পুনঃশিকার ত্রুটি পান IEnumerable

কোডের উদাহরণ:

public List<object> Foo(IEnumerable<object> objects)
{
    if (objects == null || !objects.Any())
        throw new ArgumentException();

    var firstObject = objects.First();
    var list = DoSomeThing(firstObject);        
    var secondList = DoSomeThingElse(objects);
    list.AddRange(secondList);

    return list;
}
  • আমি objectsপ্যারামিটারটিকে পরিবর্তন করতে Listএবং তারপরে সম্ভাব্য একাধিক অঙ্কটি এড়াতে পারি তবে তারপরে আমি যে সর্বোচ্চ অবজেক্টটি পরিচালনা করতে পারি তা পাই না।
  • আরেকটা জিনিস যে আমি কি করতে পারি রূপান্তর হয় IEnumerableকরার Listপদ্ধতি শুরুতে:

 public List<object> Foo(IEnumerable<object> objects)
 {
    var objectList = objects.ToList();
    // ...
 }

তবে এটি কেবল বিশ্রী

আপনি এই পরিস্থিতিতে কি করবেন?

উত্তর:


470

IEnumerableপ্যারামিটার হিসাবে গ্রহণ করতে সমস্যা হ'ল এটি কলকারীদের "আমি এটি গণনা করতে চাই" বলে tells এটি কতবার আপনি গণনা করতে চান তা তাদের জানায় না।

আমি তালিকা হিসাবে বস্তুর পরামিতি পরিবর্তন করতে এবং তারপরে সম্ভাব্য একাধিক গণনা এড়াতে পারি তবে তারপরে আমি যে সর্বোচ্চ অবজেক্টটি পরিচালনা করতে পারি তা পাই না

সর্বাধিক বস্তু গ্রহণের লক্ষ্য মহৎ, তবে এটি অনেক বেশি অনুমানের জায়গা ছেড়ে দেয়। আপনি কি সত্যিই চান যে কেউ এই পদ্ধতিতে এসকিউএল কোয়েরিতে একটি লিনকিউ পাস করুন, কেবলমাত্র এটির জন্য আপনি এটি দ্বিগুণ করতে পারেন (প্রতিটি বারের সম্ভাব্য ভিন্ন ফলাফল পেয়েছেন?)

এখানে শব্দার্থবিজ্ঞানের অনুপস্থিতি হ'ল একজন কলার, যিনি সম্ভবত পদ্ধতির বিশদটি পড়তে সময় নেন না, তিনি আপনাকে কেবল একবার পুনরাবৃত্তি করতে পারেন - তাই তারা আপনাকে একটি ব্যয়বহুল অবজেক্ট দেয়। আপনার পদ্ধতির স্বাক্ষর কোনওভাবেই নির্দেশ করে না।

পদ্ধতিতে স্বাক্ষরটি IList/ এ পরিবর্তনের মাধ্যমে ICollectionআপনি অন্তত আপনার প্রত্যাশা কী তা কলারের কাছে পরিষ্কার করবেন এবং তারা ব্যয়বহুল ভুল এড়াতে পারবেন।

অন্যথায়, বেশিরভাগ বিকাশকারী পদ্ধতিটির দিকে তাকিয়ে থাকতে পারে আপনাকে একবারেই পুনরাবৃত্তি করতে পারে। যদি গ্রহণ IEnumerableকরা এত গুরুত্বপূর্ণ .ToList()হয় তবে আপনার পদ্ধতির শুরুতে এটি করা বিবেচনা করা উচিত ।

এটি একটি লজ্জাজনক .NET- এর একটি ইন্টারফেস নেই যা আইনেম্যুয়াল + কাউন্ট + ইনডেক্সার, অ্যাড / রিমুভ ইত্যাদি পদ্ধতি ছাড়াই, যা আমার সন্দেহ এই সমস্যাটি সমাধান করবে।


31
কেবলমাত্র পাঠ্য সংগ্রহ <টি> আপনার পছন্দসই ইন্টারফেসের প্রয়োজনীয়তা পূরণ করে? এমএসডিএন.মাইক্রোসফট.এইন.উস
ড্যান ইজ

73
@ ড্যানিইলি আমি IReadOnlyCollection(T)এই ধারণাটি প্রকাশ করার জন্য সেরা ইন্টারফেস হিসাবে ( .NET 4.5 দিয়ে নতুন) পরামর্শ দিচ্ছি যে এটি IEnumerable(T)একাধিকবার গণনা করার উদ্দেশ্যে। যেমন এই উত্তরটি বলেছে, IEnumerable(T)নিজেই এত সাধারণ যে এটি পুনরায় সেটযোগ্য সামগ্রীগুলিকেও উল্লেখ করতে পারে যা পার্শ্ব প্রতিক্রিয়া ছাড়াই আবার গণনা করা যায় না। তবে IReadOnlyCollection(T)পুনরায় পুনরুক্তি বোঝায়।
বিনকি

1
আপনি যদি .ToList()পদ্ধতিটির শুরুতে এটি করেন, আপনাকে প্রথমে প্যারামিটারটি শূন্য নয় কিনা তা পরীক্ষা করে দেখতে হবে। এর অর্থ আপনি দুটি স্থান পাবেন যেখানে আপনি একটি আর্গুমেন্ট এক্সেক্সপশন নিক্ষেপ করেছেন: যদি প্যারামিটারটি শূন্য থাকে এবং যদি এতে কোনও আইটেম না থাকে।
comecme

আমি পড়তে এই নিবন্ধটি এর শব্দার্থবিদ্যা উপর IReadOnlyCollection<T>বনাম IEnumerable<T>এবং তারপর পোস্ট একটি প্রশ্ন ব্যবহার সম্পর্কে IReadOnlyCollection<T>একটি প্যারামিটার হিসাবে, এবং যা ক্ষেত্রেই। আমি মনে করি অবশেষে আমি যে সিদ্ধান্তে এসেছি তা হ'ল অনুসরণ করার যুক্তি। এটি গণনা প্রত্যাশিত যেখানে ব্যবহার করা উচিত, একাধিক গণনা থাকতে পারে এমনভাবে অগত্যা নয়
নিও

32

যদি আপনার ডেটা সর্বদা পুনরাবৃত্তিযোগ্য হতে চলেছে তবে সম্ভবত এটি সম্পর্কে চিন্তা করবেন না। তবে, আপনি এটিও আনরোল করতে পারেন - আগত ডেটা বড় হতে পারে তবে এটি বিশেষত কার্যকর (উদাহরণস্বরূপ, ডিস্ক / নেটওয়ার্ক থেকে পড়া):

if(objects == null) throw new ArgumentException();
using(var iter = objects.GetEnumerator()) {
    if(!iter.MoveNext()) throw new ArgumentException();

    var firstObject = iter.Current;
    var list = DoSomeThing(firstObject);  

    while(iter.MoveNext()) {
        list.Add(DoSomeThingElse(iter.Current));
    }
    return list;
}

দ্রষ্টব্য আমি ডসোমথিংএলস এর শব্দার্থকে কিছুটা পরিবর্তন করেছি, তবে এটি মূলত অনিয়ন্ত্রিত ব্যবহার দেখানোর জন্য। উদাহরণস্বরূপ, আপনি পুনরাবৃত্তি পুনরায় মোড়ানো করতে পারে। আপনি এটিকে একটি পুনরুদ্ধারকারী ব্লকও বানাতে পারেন, এটি দুর্দান্ত হতে পারে; তারপরে কোনও নেই list- এবং আপনি yield returnযে আইটেমগুলি পাবেন সেভাবে আপনি ফিরিয়ে আনার তালিকায় যুক্ত না করেই চাইবেন ।


4
+1 ওয়েল এটি একটি খুব সুন্দর কোড, তবে- আমি কোডটির pretiness এবং পাঠযোগ্যতা আলগা করি।
gdoron মনিকা

5
@gdoron সব কিছুই সুন্দর নয়; p যদিও আমি উপরেরটি অপঠনযোগ্য নয়। বিশেষত যদি এটি একটি পুনরাবৃত্তকারী ব্লক তৈরি করা হয় - বেশ সুন্দর, আইএমও।
মার্ক গ্র্যাভেল

আমি কেবল লিখতে পারি: "var অবজেক্টলিস্ট = অবজেক্টস o টোললিস্ট ();" এবং সমস্ত এনামুলেটর হ্যান্ডলিং সংরক্ষণ করুন।
gdoron মনিকা

10
@ জিডোরন প্রশ্নে আপনি স্পষ্টভাবে বলেছেন যে আপনি এটি করতে চান না; p এছাড়াও, কোনও টোলিস্ট () পদ্ধতির সাথে মানানসই নাও হতে পারে যদি ডেটা খুব বড় (সম্ভাব্য, অসীম - সিকোয়েন্সগুলি সীমাবদ্ধ করার প্রয়োজন হয় না, তবে পারে এখনও পুনরাবৃত্তি করা)। শেষ পর্যন্ত, আমি কেবল একটি বিকল্প উপস্থাপন করছি। এটির ওয়্যারেন্টেড আছে কিনা তা দেখতে কেবল আপনি সম্পূর্ণ প্রসঙ্গটি জানেন। আপনার ডেটা যদি পুনরাবৃত্তিযোগ্য হয় তবে অন্য একটি বৈধ বিকল্প হ'ল: কিছু পরিবর্তন করবেন না! পরিবর্তে আর # উপেক্ষা করুন ... এটি সমস্ত প্রসঙ্গে নির্ভর করে।
মার্ক গ্র্যাভেল

1
আমি মনে করি মার্কের সমাধানটি বেশ সুন্দর। ১. 'তালিকা' কীভাবে সূচনা করা হয়েছে তা খুব স্পষ্ট, তালিকাটি কীভাবে পুনরাবৃত্তি করা হয়েছে তা খুব স্পষ্ট, এবং তালিকার কোন সদস্যরা কাজ করছেন তা খুব স্পষ্ট।
কিথ হফম্যান

8

পরিবর্তে পদ্ধতিতে স্বাক্ষর ব্যবহার IReadOnlyCollection<T>বা ব্যবহার করাIReadOnlyList<T>IEnumerable<T> করার ক্ষেত্রে স্পষ্ট করে বলার সুবিধা রয়েছে যে আপনাকে পুনরাবৃত্ত হওয়ার আগে গণনাটি পরীক্ষা করতে হবে, বা অন্য কোনও কারণে একাধিকবার পুনরাবৃত্তি করতে হবে।

তবে তাদের একটি বিশাল নেতিবাচক সমস্যা রয়েছে যা আপনি যদি আপনার কোডটিকে ইন্টারফেসগুলি ব্যবহার করার জন্য পুনরায় ব্যবহারের চেষ্টা করেন, উদাহরণস্বরূপ এটিকে আরও পরীক্ষামূলক এবং গতিশীল প্রক্সিংয়ের জন্য বন্ধুত্বপূর্ণ করার জন্য। মূল বক্তব্যটি হ'ল IList<T>উত্তরাধিকার সূত্রে প্রাপ্ত নয়IReadOnlyList<T> এবং একইভাবে অন্যান্য সংগ্রহ এবং তাদের কেবল পঠনযোগ্য ইন্টারফেসগুলির জন্য। (সংক্ষেপে এটি হ'ল। নেট ET.৪ পূর্ববর্তী সংস্করণগুলির সাথে এবিআইয়ের সামঞ্জস্য রাখতে চেয়েছিল that তবে তারা এটি নেট নেটওয়ার্কে পরিবর্তনের সুযোগও নেন নি ))

এর অর্থ হ'ল যদি আপনি IList<T>প্রোগ্রামটির কিছু অংশ থেকে একটি পেয়ে থাকেন এবং এটি অন্য অংশে পাস করতে চান যা একটি প্রত্যাশা করে IReadOnlyList<T>, আপনি পারবেন না! আপনি যাইহোক একটি IList<T>হিসাবে পাস করতে পারেনIEnumerable<T>

শেষে, IEnumerable<T> সমস্ত সংগ্রহ ইন্টারফেস সহ সমস্ত .NET সংগ্রহ দ্বারা সমর্থিত একমাত্র পঠনযোগ্য ইন্টারফেস। অন্য কোনও বিকল্প আপনাকে কামড়ানোর জন্য ফিরে আসবে কারণ আপনি বুঝতে পেরেছেন যে আপনি কোনও স্থাপত্য পছন্দ থেকে নিজেকে আটকিয়ে রেখেছেন। সুতরাং আমি মনে করি যে এটি সুনির্দিষ্টভাবে ফাংশন স্বাক্ষরগুলিতে ব্যবহার করার জন্য যে আপনি কেবলমাত্র পঠনযোগ্য সংগ্রহ চান তা প্রকাশ করুন।

(দ্রষ্টব্য যে আপনি সর্বদা একটি IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)এক্সটেনশন পদ্ধতি লিখতে পারেন যা অন্তর্নিহিত প্রকারটি উভয় ইন্টারফেসকে সমর্থন করে তবে সাধারণ কাস্ট করে, তবে রিফ্যাক্টরিংয়ের সময় আপনাকে সর্বত্র এটি যুক্ত করতে হবে, যেখানে IEnumerable<T>সর্বদা সামঞ্জস্যপূর্ণ is)

সর্বদা এটি নিখুঁত নয়, আপনি যদি ডাটাবেস-ভারী কোড লিখতে থাকেন যেখানে দুর্ঘটনাযুক্ত একাধিক গণনা বিপর্যয় দেখা দেয় তবে আপনি অন্য কোনও বাণিজ্য-বাণিজ্য পছন্দ করতে পারেন।


2
+1 একটি পুরানো পোস্টে আসার জন্য এবং .NET প্ল্যাটফর্মের দ্বারা সরবরাহিত নতুন বৈশিষ্ট্যগুলি (যেমন IReadOnly Colલેક્ <<>)
কেভিন আভিগন

4

যদি উদ্দেশ্যটি হ'ল মারক গ্র্যাভেলের উত্তরটি পড়ার চেয়ে একাধিক গণনা রোধ করা তবে একই শব্দার্থিকতা বজায় রাখা আপনি সহজভাবে অনর্থক Anyএবং Firstকলগুলি সরাতে পারেন এবং সাথে যেতে পারেন:

public List<object> Foo(IEnumerable<object> objects)
{
    if (objects == null)
        throw new ArgumentNullException("objects");

    var first = objects.FirstOrDefault();

    if (first == null)
        throw new ArgumentException(
            "Empty enumerable not supported.", 
            "objects");

    var list = DoSomeThing(first);  

    var secondList = DoSomeThingElse(objects);

    list.AddRange(secondList);

    return list;
}

দ্রষ্টব্য, এটি ধরে নিয়েছে যে আপনি IEnumerableজেনেরিক নন বা কমপক্ষে একটি রেফারেন্স টাইপ হিসাবে সীমাবদ্ধ।


2
objectsএখনও ডোমোমিংথিংয়ে পাস হচ্ছে যা একটি তালিকা ফিরিয়ে দিচ্ছে, সুতরাং আপনি এখনও দু'বার গণনার সম্ভাবনা এখনও রয়েছেন। যে কোনও উপায়ে যদি সংগ্রহটি এসকিউএল কোয়েরিতে একটি লাইনকিউ হয়, আপনি ডিবিটিকে দুবার আঘাত করেছেন।
পল স্টোভেল

1
@ পলস্টোভেল, এটি পড়ুন: "যদি মার্ক গ্র্যাভেলের উত্তরটি পড়ার চেয়ে একাধিক গণনা রোধ করা সত্যিই লক্ষ্য হয় তবে" =)
জিডোরন মনিকে

এটি খালি তালিকার ব্যতিক্রম নিক্ষেপ সম্পর্কিত অন্য কিছু স্ট্যাকওভারফ্লো পোস্টগুলিতে পরামর্শ দেওয়া হয়েছে এবং আমি এখানে এটি প্রস্তাব করব: খালি তালিকায় ব্যতিক্রম কেন ছুঁড়ে ফেলুন? একটি খালি তালিকা পান, একটি খালি তালিকা দিন। দৃষ্টান্ত হিসাবে, এটি বেশ সোজা। যদি কলকারী জানেন না যে তারা একটি খালি তালিকা পাস করছেন, এতে সমস্যা আছে।
কিথ হফম্যান

@ কিথ হফম্যান, নমুনা কোড ওপি পোস্ট করা কোডের মতো একই আচরণ বজায় রেখেছে, যেখানে ব্যবহারটি Firstখালি তালিকার ব্যতিক্রম ছুঁড়ে ফেলবে। আমি কখনই খালি তালিকায় ব্যতিক্রম ছুঁড়ে ফেলা বা খালি তালিকায় সর্বদা ব্যতিক্রম ছুঁড়ে ফেলা বলা সম্ভব বলে মনে করি না। এটি নির্ভর করে ...
জোও অ্যাঞ্জেলো

মোটামুটি যথেষ্ট জোয়াও। আপনার পোস্টের সমালোচনার চেয়ে চিন্তার জন্য বেশি খাবার।
কিথ হফম্যান

3

আমি এই পরিস্থিতিতে আইনিউমেবল এবং আইলিস্টের সাথে সাধারণত আমার পদ্ধতিটি ওভারলোড করি।

public static IEnumerable<T> Method<T>( this IList<T> source ){... }

public static IEnumerable<T> Method<T>( this IEnumerable<T> source )
{
    /*input checks on source parameter here*/
    return Method( source.ToList() );
}

আইইনুমারেবল কল করা একটি।

প্রোগ্রামার উচ্চতর স্তরে .ToList () নির্বাচন করতে পারে যদি একাধিক ক্রিয়াকলাপ সংঘবদ্ধ হয়ে থাকে এবং তারপরে IList ওভারলোডকে কল করতে পারেন বা আমার আইএনউমারেবল ওভারলোডটিকে এটি যত্ন নিতে দেয়।


20
এটা ভয়াবহ।
মাইক চেম্বারলাইন

1
@ মাইকচেম্বারলাইন হ্যাঁ, এটি একইসাথে ভয়াবহ এবং সম্পূর্ণ পরিষ্কার। দুর্ভাগ্যক্রমে কিছু ভারী দৃশ্যের এই পদ্ধতির প্রয়োজন।
মাওরো সাম্পিয়েটরো

1
কিন্তু তারপরে আপনি যখনই এটি পরীক্ষামূলক প্যারামেরাইটিজড পদ্ধতিতে পাস করবেন তখন আপনি অ্যারেটি পুনরায় তৈরি করবেন। আমি এই বিষয়ে @ মাইক চেম্বারলাইন এর সাথে একমত, এটি একটি ভয়ানক পরামর্শ।
Krythic

2
@ ক্রিথিক যদি আপনার তালিকাটি পুনরায় তৈরি করার প্রয়োজন না হয় তবে আপনি অন্য কোথাও একটি টোললিস্ট সঞ্চালন করুন এবং আইএলিস্ট ওভারলোড ব্যবহার করুন। এটাই এই সমাধানের মূল বিষয়।
মুরো সাম্পিয়েট্রো

0

আপনার যদি কেবলমাত্র প্রথম উপাদানটি পরীক্ষা করতে হয় তবে আপনি পুরো সংগ্রহটি পুনরাবৃত্তি না করেই উঁকি দিতে পারেন:

public List<object> Foo(IEnumerable<object> objects)
{
    object firstObject;
    if (objects == null || !TryPeek(ref objects, out firstObject))
        throw new ArgumentException();

    var list = DoSomeThing(firstObject);
    var secondList = DoSomeThingElse(objects);
    list.AddRange(secondList);

    return list;
}

public static bool TryPeek<T>(ref IEnumerable<T> source, out T first)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    IEnumerator<T> enumerator = source.GetEnumerator();
    if (!enumerator.MoveNext())
    {
        first = default(T);
        source = Enumerable.Empty<T>();
        return false;
    }

    first = enumerator.Current;
    T firstElement = first;
    source = Iterate();
    return true;

    IEnumerable<T> Iterate()
    {
        yield return firstElement;
        using (enumerator)
        {
            while (enumerator.MoveNext())
            {
                yield return enumerator.Current;
            }
        }
    }
}

-3

প্রথমত, সেই সতর্কতা সর্বদা এতটা বোঝায় না। এটি কোনও পারফরম্যান্স বোতল ঘাড় নয় তা নিশ্চিত করার পরে আমি সাধারণত এটি অক্ষম করি। এর অর্থ IEnumerableহ'ল দু'বার মূল্যায়ন করা হয়, সাধারণত যদি সমস্যা থাকে না তবেevaluation নিজে নিজে দীর্ঘ সময় নেয় । এমনকি যদি এটি দীর্ঘ সময় নেয় তবে এক্ষেত্রে আপনার প্রথমদিকে কেবলমাত্র একটি উপাদান ব্যবহার করা উচিত।

এই পরিস্থিতিতে আপনি আরও শক্তিশালী লিনক এক্সটেনশন পদ্ধতিগুলি ব্যবহার করতে পারেন।

var firstObject = objects.First();
return DoSomeThing(firstObject).Concat(DoSomeThingElse(objects).ToList();

IEnumerableকিছু ঝামেলা নিয়ে এই ক্ষেত্রে কেবল একবার মূল্যায়ন করা সম্ভব তবে প্রথমে প্রোফাইলটি দেখুন এবং দেখুন এটি সত্যিই কোনও সমস্যা কিনা।


15
আমি আইকিউয়ারেবলের সাথে অনেক কাজ করি এবং এই ধরণের সতর্কতাগুলি বন্ধ করা খুব বিপজ্জনক।
gdoron মনিকা

5
দুবার মূল্যায়ন করা আমার বইগুলির একটি খারাপ জিনিস। যদি পদ্ধতিটি অনুমিত হয় তবে আমি এসকিউএল কোয়েরিতে একটি লিনকিউ পাস করতে প্ররোচিত হতে পারি, যার ফলে একাধিক ডিবি কল আসে। যেহেতু পদ্ধতির স্বাক্ষরটি এটি দু'বার গণনা করতে পারে তা নির্দেশ করে না তাই এটি স্বাক্ষর পরিবর্তন করতে হবে (আইলিস্ট / আইকোলিকেশন গ্রহণ করুন) অথবা কেবল একবার পুনরাবৃত্তি করতে হবে
পল স্টোভেল

4
প্রারম্ভিক এবং অপ্রয়োজনীয় পাঠযোগ্যতার অনুকূলকরণ এবং এরপরে এমন অপ্টিমাইজেশানগুলি করার সম্ভাবনা যা আমার বইতে আরও খারাপ।
vidtige

আপনি একটি ডাটাবেস কোয়েরি পেয়েছেন, তখন নিশ্চিত এটি শুধুমাত্র মূল্যায়ন হচ্ছে উপার্জন একবার ইতিমধ্যে একটি পার্থক্য তোলে। এটি কেবলমাত্র কিছু তাত্ত্বিক ভবিষ্যতের সুবিধা নয়, এটি এখনই একটি পরিমাপযোগ্য বাস্তব বেনিফিট। শুধু তাই নয়, একবারে মূল্যায়ন করা কেবল পারফরম্যান্সের পক্ষে নয়, এটি সঠিকতার জন্যও প্রয়োজন। দু'বার একটি ডেটাবেস কোয়েরি কার্যকর করা বিভিন্ন ফলাফল আনতে পারে, কারণ কেউ কেউ দুটি ক্যোয়ারী মূল্যায়নের মধ্যে একটি আপডেট কমান্ড জারি করতে পারে।

1
@hvd আমি এই জাতীয় কোনও জিনিস দেওয়ার প্রস্তাব দিইনি। অবশ্যই আপনি গণনা করা উচিত নয় IEnumerablesযা প্রতিবার আপনি এটির পুনরুক্তি করেন বা পুনরাবৃত্তি করতে সত্যই দীর্ঘ সময় নেয় তা বিভিন্ন ফলাফল দেয়। মার্ক গ্র্যাভেলস উত্তর দেখুন - তিনি প্রথম এবং সর্বাগ্রে "সম্ভবত চিন্তা করবেন না" বলেও জানিয়েছেন। জিনিসটি হ'ল - অনেক বার আপনি এটি দেখলে আপনার উদ্বিগ্ন হওয়ার কিছুই নেই।
vidtige
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.