Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"ItemRef" - Ref element accessor for types that already have an indexer. #24110

Closed
VSadov opened this issue Nov 11, 2017 · 35 comments
Closed

"ItemRef" - Ref element accessor for types that already have an indexer. #24110

VSadov opened this issue Nov 11, 2017 · 35 comments
Milestone

Comments

@VSadov
Copy link
Member

VSadov commented Nov 11, 2017

We have a number of existing indexable types that could benefit from a ref-returning element accessor. For example ImmutableArray.

A typical problem is that such types already have a get/set indexer, so the only way to add an indexed byref access to elements is to add a ref returning method.
I believe this will happen one way or another, since there is a motivation. So, I think we should standardize on the name of such method.

Here I want to propose the following convention:

        // when container is readonly - I.E. ImmutableArray<T>
        public ref readonly T ItemRef(int index);

        // when container is writeable - I.E. List<T>
        public ref T ItemRef(int index);

The alternatives could be

  • “ElementAt” – often already taken
  • “Item” – clashes with the underlying name of the indexer
  • “Address” - could work too, but I think “ItemRef” reads a lot better.
  • “ItemAt” – original proposed name, "ItemRef" still seems better

Examples of uses:

    public class Program
    {
        static void Main(string[] args)
        {
            var o = new MyCollection<int>();

            // use for assignment
            o.ItemRef(1) = 42;

            // bind to a ref local
            ref var first = ref o.ItemRef(1);
            first = 42;

            // read indirectly. (assume the API takes "in" parameter)
            Print(o.ItemRef(1));
        }
    }

    class MyCollection<T>
    {
        public ref T ItemRef(int index) => throw null;
    }

Once we agree on the name/pattern we can enter separate bugs for particular APIs.

@VSadov VSadov changed the title PROPSAL: "ItemAt" - Ref element accessor for collections that already have an indexer. PROPSAL: "ItemAt" - Ref element accessor for types that already have an indexer. Nov 11, 2017
@VSadov
Copy link
Member Author

VSadov commented Nov 11, 2017

@benaadams
Copy link
Member

Sounds good

Address isn't a very .NET type of name; ItemRef/ItemReference would match it more, also clashes a bit with VB.NET's AddressOf

ItemAt I think is good though.

@benaadams
Copy link
Member

benaadams commented Nov 11, 2017

Was a suggestion of xByRef for Dictionary https://github.com/dotnet/corefx/issues/21240, https://github.com/dotnet/corefx/issues/20684

Dictionary<TKey, TVal>
{
    public ref TValue GetValueByRef(TKey key) // throws if key not found
    public ref TValue TryGetValueByRef(TKey key, out bool found) 
    public ref TValue TryGetValueByRefOrAdd(TKey key, out bool found, TValue setVal)
}

So also ItemByRef(...) or GetValueByRef(...) are options

Might be good to have something that also works well with a Try prefix?

@Joe4evr
Copy link
Contributor

Joe4evr commented Nov 11, 2017

o.ItemAt(1) = 42;

I'm not sure how I feel about this, it just looks really weird.

Other than that, would be nice to have this with specialized implementations since ElementAt is a LINQ method, so it's frequently a toss up for how it'll perform.

@benaadams
Copy link
Member

benaadams commented Nov 11, 2017

I'm not sure how I feel about this, it just looks really weird.

o.ItemAt(1) = 42;

Could introduce named indexers in C# as per VB.NET so it could be one of

o.ItemAt[1] = 42;
o.ItemRef[1] = 42;
o.ItemByRef[1] = 42;
o.ByRef[1] = 42;
class MyCollection<T>
{
    public T this[int index] { get; set; }
    public ref T ItemByRef[int index] => throw null;
}

@JonHanna
Copy link
Contributor

Well, I've wanted named indexers since the public beta of C# 1, so my instinct is to favour that, though it opens its own can of worms. My initial thought to the opening post here was that it was another place to wish for named indexers, though.

@mikedn
Copy link
Contributor

mikedn commented Nov 11, 2017

I think that having ref in the name is good so I'll vote for ItemRef, RefItem etc. Though I'm not convinced that adding this to general purpose collections like List<T> is a good idea.

@jkotas
Copy link
Member

jkotas commented Nov 11, 2017

adding this to general purpose collections like List is a good idea

Agree. It is hard to explain and enforce for how long the ref is valid.

@jkotas jkotas changed the title PROPSAL: "ItemAt" - Ref element accessor for types that already have an indexer. PROPOSAL: "ItemAt" - Ref element accessor for types that already have an indexer. Nov 11, 2017
@JonHanna
Copy link
Contributor

Is "ref" language-neutral across .NET languages (including those who don't support ref returns, in case they do in the future)?

@benaadams
Copy link
Member

benaadams commented Nov 11, 2017

VB.NET has ByRef though it can only consume ref returns:

Visual Basic does not allow you to author methods with reference return values, but it does allow you to consume reference return values.

and F# has ref and byref; byref being the ref return consumer

Starting with F# 4.1, you can consume ref returns generated in C#. The result of such a call is a byref<_> pointer.

@VSadov
Copy link
Member Author

VSadov commented Nov 12, 2017

Adding the helper to resizable types such as List and Dictionary is indeed controversial since it is possible to cause a resize while holding a reference.

We are not discussing the set of types that could use this though.

So far I hear two popular choices:

@VSadov
Copy link
Member Author

VSadov commented Nov 12, 2017

  • ItemAt

@VSadov
Copy link
Member Author

VSadov commented Nov 12, 2017

  • ItemRef

@benaadams
Copy link
Member

Like what you did there 😄

@jaredpar
Copy link
Member

Adding the helper to resizable types such as List and Dictionary is indeed controversial since it is possible to cause a resize while holding a reference.

This exact discussion came up, at length over and over again, when we did ref returns in Midori. The result is the controversy lasted until we actually checked in the change to expose the ref values from List<T>. After that our code got faster, the debate ended and the hypothetical "using a ref after resize" never actually turned into a real issue.

@VSadov VSadov changed the title PROPOSAL: "ItemAt" - Ref element accessor for types that already have an indexer. PROPOSAL: "ItemRef" - Ref element accessor for types that already have an indexer. Nov 13, 2017
@benaadams
Copy link
Member

This exact discussion came up, at length over and over again, when we did ref returns in Midori. The result is the controversy lasted until we actually checked in the change to expose the ref values from List. After that our code got faster, the debate ended and the hypothetical "using a ref after resize" never actually turned into a real issue.

In "Unite Austin 2017 - Bringing Modern .NET to Unity" they even talk about how to create your own value list with ref returns as one of the exciting new features 😄 https://youtu.be/vJqWn74eCOI?t=30m15s

@JonHanna
Copy link
Contributor

https://github.com/dotnet/corefx/issues/25189#issuecomment-343652212

Could introduce named indexers in C# as per VB.NET so it could be one of

Is this at all likely in anything remotely considered short-term? My number 1 preference would be ItemRef as a named indexer, followed by ItemAt as a named indexer. If we aren't at all likely to see it soon though, I wouldn't want to keep waiting on it.

@jkotas
Copy link
Member

jkotas commented Nov 15, 2017

how to create your own value list with ref returns

It is absolutely fine to do in custom collections that are optimized for specialized cases like large value types. The mainstream .NET collections are optimized for productivity, not for the last bit of performance in specialized cases.

@benaadams
Copy link
Member

The mainstream .NET collections are optimized for productivity,

Currently for a value type you need to do:

for (var i = 0; i < list.Count; i++)
{
    var val = list[i];
    val.X = 0.0;
    list[i] = val;
}

As well as being more perfomant, it is also more productive to mirror an array's behaviour:

for (var i = 0; i < list.Count; i++)
{
    list[i].X = 0.0;
}

@jaredpar
Copy link
Member

jaredpar commented Nov 15, 2017

Getting back to the proposal though. This is meant to provide guidance for the pattern to use for ref accessors on existing types to ensure consistency when we choose to do so. The proposal isn't meant to debate which types we add it to. I'm sure we'll have plenty of fun doing that later 😄

There seem to be two suggestions now for the name: ItemRef and ItemAt.

I'm slightly partial to ItemRef because the name is more descriptive of the action here. I would also be fine with ItemAt.

@benaadams
Copy link
Member

ItemRef does seem to have the edge:

image

@VSadov
Copy link
Member Author

VSadov commented Nov 15, 2017

Yes. I think we are nearly done here and ItemRef is going to be the name.

@benaadams
Copy link
Member

What would a Try method be e.g. dictionary?

Something like TryGetValueByRef; current being TryGetValue

@terrajobst
Copy link
Member

While strictly speaking not an API suggestion, it's a subject we should review as part of the API review process.

@VSadov
Copy link
Member Author

VSadov commented Nov 15, 2017

Sure. As a part of the review we need to settle on:

  1. General pattern for adding a ref accessor when indexer already present.
    Here we propose a ref [readonly] returning method with a name we agree on.
  2. Name of the method
    Discussion has converged on ItemRef
  3. Rough guidance or set of principles on what types should have this.
    I would suggest to go through a list of representative types and discuss/record whether we want to add a ref accessor or not and why.

@VSadov
Copy link
Member Author

VSadov commented Nov 15, 2017

=== The list of "interesting" types :

  • ImmutableArray<T>
  • ImmutableList<T>
  • ArraySegment<T>
  • Stack<T>
  • Queue<T>
  • List<T>
  • ImmutableArray<T>.Builder<T>
  • StringBuilder
  • ImmutableDictionary<K, V>
  • Dictionary<K, V>

== Not very useful / problematic

  • String
  • Sets, especially sorted sets
  • Concurrent collections
  • Generally any data structure whose internal organization depends on the value

@benaadams
Copy link
Member

String returning a ref readonly char probably isn't useful over just char? Unless there was a corresponding interface that it conformed to.

StringBuilder returning a ref char would have some utility.

@terrajobst
Copy link
Member

terrajobst commented Nov 21, 2017

Video

For data types like Stack<T> and Queue<T> the proposed name ItemRef wouldn't work. Thus, this would be more like a pattern where the name depends on the concept you add a ref-returning method for:

  • Indexers -> ItemRef()
  • Peek() -> PeekRef()

@karelz karelz changed the title PROPOSAL: "ItemRef" - Ref element accessor for types that already have an indexer. "ItemRef" - Ref element accessor for types that already have an indexer. Nov 21, 2017
@karelz
Copy link
Member

karelz commented Nov 21, 2017

FYI: The API review discussion was recorded - see https://youtu.be/o5JFZtgaJLs?t=2879 (43 min duration)

@OmarTawfik OmarTawfik self-assigned this Dec 1, 2017
@stephentoub
Copy link
Member

Moving this back to api-ready-for-review to discuss @jkotas' feedback: dotnet/coreclr#17405 (comment)

@stephentoub stephentoub reopened this Jun 9, 2018
@OmarTawfik
Copy link
Contributor

@stephentoub not sure why you re-opened this. The concern in the other PR was about other collections (not immutable ones). Did I miss anything?

@stephentoub
Copy link
Member

The concern in the other PR was about other collections (not immutable ones). Did I miss anything?

This issue is the one listing out the types to have Item/ValueRef added to it, no?
https://github.com/dotnet/corefx/issues/25189#issuecomment-344688581

@OmarTawfik OmarTawfik removed their assignment Jun 26, 2018
@terrajobst
Copy link
Member

terrajobst commented Jun 26, 2018

Video

This issue was about the naming convention (concept suffixing with Ref); it wasn't a particular set of members. It seems there are issues with the particular APIs being added (specifically List<T> and Dictionary<K,V>.)

Please file API review requests for the specific members so we can review those (or alternatively update the issue description to have their signatures and reopen this one).

@stephentoub
Copy link
Member

@terrajobst, so what did it mean for this to be tagged as api-approved? It was the concept being approved, not an API? So the APIs in dotnet/coreclr#17405 weren't approved? What about the APIs in dotnet/corefx#25738 that were already merged and cite this issue?

@Neo-Ciber94
Copy link

For shorter other possible signature could be just At or Get

public class List<T>
{
        public ref T At(int index);
        //or
        public ref T Get(int index);
}

Also RefAt, GetRef

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 2.1.0 milestone Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 19, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests