TransWikia.com

C# Why should i limit myself to List or Stack ? ( instead of having both)

Software Engineering Asked by ran keren on January 21, 2021

List is implemented in C# exactly as Stack, see:

Given that:

Why should developers choose, List or Stack, while in fact they can have "2 in 1".
(Implementing Pop for List, would make it 2-in-1! ).

Coming from more flexible languages, I don’t want to commit to a limiting-collection-type. Starting with Stack, maybe later as problem changes, I’ll need to access an [index], alternatively w/Lists, maybe later need Pop or List1.Add (List2.Pop()) often.

  1. Why doesn’t RemoveAt() return a value? What damage would happen if it returned a value (as actually done in Java ArrayList)?

  2. Why no Pop()?

Looking at Python, Perl, Ruby and ‘e’, one does wonder how come a C# List doesn’t have Pop(). I want the "Swiss army knife" of arrays. i.e. List! It’s convenient to use just one language construct, and avoid conversions, etc. As List is implemented using array and supports Add in O(1) (Add is just a different name for Push), why wasn’t Pop added as well?

  1. Why do we need a C# Stack when List could easily do all Stack does, and more?

    Easy way to pop ‘d make me always use List, hence saving the need of conversions/casting between List and Stack.

Am i the only C# dude, that wants a (slightly) more powerful List ? ( instead of having 2 weaker concepts and having to convert between them ).
( other than that c# is really good).

Update : Wow !! Thanks to this thread Microsoft had decided to add Pop to C# list !! https://github.com/dotnet/runtime/issues/31828.
many thanks to the community, especially to user @Theraot.

4 Answers

Why should developers choose, List or Stack, while in fact they can have "2 in 1". (Implementing Pop for List, would make it 2-in-1! ).

This could mean bad encapsulation. You pass your Listack to third party code, and it uses in ways that you do not expect, resulting in an invalid state for your code. There is value in limiting what objects can do. It is good for defensive programming. See also interface segregation.


Coming from more flexible languages, I don't want to commit to a limiting-collection-type. Starting with Stack, maybe later as problem changes, I'll need to access an [index], alternatively w/Lists, maybe later need Pop or List1.Add (List2.Pop()) often.

Right. You may decide in the future that the type or interface you are using is not adequate and want to change it. And of course we do not want this change to propagate.

Well, if your methods receive objects by an interface, you can change the type without changing where you use it. Then, when you need to change to a type that has the same capabilities (and thus the same interfaces) plus some new ones... well, all methods that used the object before should still work, given that they use an interface that you are not removing... you are adding. So the change does not propagate out of control. See also interface segregation.


Why doesn't RemoveAt() return a value?

As Raymond Chen would put it:

features start out nonexistent and somebody has to make them happen.

-- source

There is value in asking why the other languages do it, and whatever or not those reasoning apply to .NET. Oh, yes, we are talking about the standard library, not the language C#. If that reasoning applies… well...


What damage would happen if it returned a value (as actually done in Java ArrayList)?

At source level, there should be no problem. Nobody is using the return value of RemoveAt, given that it hasn’t one, and thus, no source code would be broken.

However, IL is a stack language. And the return value would be placed in the stack, and had to be discarded with a pop. Thus, this change would break assemblies. We are talking of recompiling them. Existing assemblies could continue to work in an old runtime.

That of course, does not explain why they didn't do it in the first place.

With that said, a new method could be added to List<T> that does what you want...


Why no Pop()?

Right. Like adding a List<T>.Pop method. Adding that should be possible.

You could try posting an issue to dotnet/runtime, maybe a merge request.

If they don't want to do it, they will tell you why.

And perhaps in the next release…

Why wait? You could implement Pop for List<T> as an extension method. I like David Arno's solution.


Why do we need a C# Stack when List could easily do all Stack does, and more?

You don't. And you do not need List either, you can work with arrays. Stack exists for convenience of its use cases, and so does List. I can only guess that the uses cases you have in mind where not considered for List.

However, we may want the distinction. The more limited is an object, the more certain we are about how it will be used. And thus, we may want a limited object to ensure it is not misused/abused.


Easy way to pop 'd make me always use List, hence saving the need of conversions/casting between List and Stack

I would consider making an adapter for List that wraps it and works like a Stack. You may even provide IProducerConsumerCollection<T>.


Am i the only C# dude, that wants a (slightly) more powerful List ?

No. Some people want, and have created other kinds of collections. There are Nugets out there… Although you are the first I have seen to want this particular change.

I don't think "List with Pop" are good search terms, I didn’t find a library for .NET that have that.

Perhaps what you want already exists. Perhaps you can create it and publish it for every body to use. Remember that features start out nonexistent and somebody has to make them happen.

Answered by Theraot on January 21, 2021

If you want a "slightly more powerful list", then implement it. Make use of extension methods to add what you want to that class. For example, the following code will give you a list that returns the value when removing an element and provides Push and Pop:

using System;
using System.Collections.Generic;

public static class ListExtensions 
{
    public static T FetchAndRemoveAt<T>(this List<T> list, int index)
    {
        if (index >= list.Count) throw new ArgumentOutOfRangeException(nameof(index));

        var value = list[index];
        list.RemoveAt(index);

        return value;
    }

    public static void Push<T>(this List<T> list, T value) => list.Add(value);

    public static T Pop<T>(this List<T> list) => list.FetchAndRemoveAt(list.Count-1);
}

It's a naïve implementation that's certainly not thread-safe, but it may meet your needs. If not, write it the way you need it to be. If you think others would find it useful, publish it as a nuget package. So rather than worrying why the designers didn't implement things the way you want, you can implement them that way very easily.

Answered by David Arno on January 21, 2021

instead of having 2 weaker concepts and having to convert between them

Well, that is actually not how I see it. Since a List allows one effectively to execute all stack-like operations, if one really needs "both concepts in one", using a List allows this, so this is IMHO not a "weaker" concept.

Of course, the equivalent of a Pop() operation on a List requires a little bit more code, but if that really bothers you, because in your specific context you need that operation frequently, you can write an extension method for it, like this:

static class ListExtensions
{
    public static T Pop<T>(this List<T> list)
    {
        int lastIdx = list.Count - 1;
        T lastItem = list[lastIdx];
        list.RemoveAt(lastIdx);
        return lastItem;
    }
}

(Similarly, you could implement something like a PopAt operation, which means a RemoveAt with returning the removed value.)

I understand your question in this sense: why did the .Net framework designers not provide such a method directly as part of the List<T> class? For getting a binding answer, you need to ask those library designers. But I can imagine the follwing potential reasons:

  • Mixing up two concepts in one class does not make the class more readable and understandable. Historically, arrays, lists and stacks are usually taught as different concepts in most CS courses, and the terminology which includes Push, Peek, and Pop is exclusively used the context of stacks. So having one class for each of these concepts fits better to this than mixing up two of those concepts in one class, even it would technically be possible

  • Any additional feature in a class requires additional effort to specify it, implement it, test it, review it, document it, release it and maintain it. So the designers are usually very reluctant to add features which can be easily added by the users of the classes by themselves.

  • The number of cases where one really needs a Pop operation on a random access list is probably small enough for justifying to let the users implement their own function in case they really require it.

Answered by Doc Brown on January 21, 2021

Part of the point of objects is to limit the things you can do with them as well as enable you to do things.

If I could add items randomly into a stack then it wouldnt be a stack.

If I pop from a list should it return the first or last item added?

Limiting behaviour and having specialised objects is a good thing because it allows you to have sensible expectations about what the object will do without knowing the detail of the implementation.

Answered by Ewan on January 21, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP