C# should support indexed property #471
-
@ygc369 commented on Tue Apr 21 2015 for example: @ygc369 commented on Thu Oct 15 2015 nobody is interested in this? @jrmoreno1 commented on Sat Mar 05 2016 Well, I like the idea, but it's been out there for a while and rejected because of "ambiguous syntax" (not sure what is ambiguous about it myself). I like the idea, because semantically an indexed property is not an item in a collection. In fact I found this while checking to make sure that it hadn't been added since the last time I thought it would make things easier. In this case I was hoping to build a facade around Session, where most of the items are not just per Session but per window/tab. Index properties would make that trivial: Session.ExtensionMethodReturnsFacade().Property[page]. @alrz commented on Sat Mar 05 2016 If the the type has an indexer you would get that syntax, class Foo { public int this[int index] { ... } }
class Bar { public Foo Foo { get; } }
var bar = new Bar();
int foo = bar.Foo[5]; And if you define @jrmoreno1 commented on Sat Mar 05 2016 @alrz and you see value in writing and instantiating a class in order to return an indexed value? The class Foo is never going to be used for anything other than the property Foo in Bar. As for the ambiguity -- I still don't see it, at least as far as the compiler is concerned. For people looking at the line in isolation, it wouldn't be clear whether it was an indexed property or a property for an indexer or Foo is just an array or dictionary, but I don't see how that matters. Any way you look at it, it is returning a value from something else. Looking at the definition will tell you what it is. @HaloFour commented on Sat Mar 05 2016 From http://roslyn.codeplex.com/discussions/542484
And that nested type can be a struct, no allocations necessary. @alrz commented on Sat Mar 05 2016
See, class Foo { public Bar this[int index] { ... } }
class Bar { public Foo Foo[int index] { ... } }
var bar = new Bar();
var foo = bar.Foo[5]; // which indexer I'm calling?
If it doesn't matter why don't you use a method instead? You are basically suggesting a method that has class Bar { public Foo Foo[int index] { ... } }
class Bar { public Foo Foo(int index) { ... } } Why is this important then? @alrz commented on Sat Mar 05 2016 As for assignment, @jrmoreno1 commented on Sun Mar 06 2016 @HaloFour I gave an example of creating a facade around Sesssion where a page id is used to avoid collissions between tabs. Most of the properties should be indexed. C#, unlike VB, requires that an indexed property be the default property of a class. C# is conflating two entirely separate concepts, and in doing so being unclear. @alrz in VB, where that works, it calls the indexer in Bar (which returns a Foo), not the default property of a Foo (which returns a Bar). I don't see how it could be otherwise. As for why it matters, you hit upon it in your second message: assignment. A property handles both retrieving and setting a value. Also a method that returns a value is a calculation, while a property is state. As for ref return, I know the CLR supports it, but c# doesn't, and even if it did, that's not quite the same thing as a property. @HaloFour commented on Sun Mar 06 2016 So your facade exposes a bunch of "named indexable" properties each taking the same index and each returning a different value? var x = obj.X[id];
var y = obj.Y[id]; Why would that be better than having one normal indexer which returns an object containing all of the properties for that page? var x = obj[id].X;
var y = obj[id].Y; Then you can pass all of the properties around without each consumer having to know/care about how they're indexed. Your use case sounds like the very design that the language should be discouraging. @jrmoreno1 commented on Fri Mar 18 2016 First let me say to that I understand that everything starts at -100, and this isn't a pony let alone a unicorn. I'm not trying to say "do it" but rather, "it shouldn't be rejected". Anyway... Sorta, not all of the properties will be indexed. Maybe half, pssoibly less. And doing it as you suggests works, but it creates an artificial distinction between properties on the wrapper and the indexed properties (class page with some properties). Session.w().D seems quite different from Session.w()[id].Y, as opposed to Session.w().D and Session.w().Y[id]. And I don't see why Session.w()[id].Y should be considered better than Session.w().Y[id](other than it works). The later seems more natural to me. The first seems to be saying "get the page of wrapper with the value id and then get the Y property" and the second "get the property Y of wrapper and then get the id value". But page doesn't really exist, it is an additional abstraction that is necessary only in order to make the access actually work. And since page doesn't model any real object and the properties aren't related in any way, the abstraction doesn't help you reason about the program. Again, I'm not saying this is a must-have or the best way (let alone the only) way to solve any particular problem. Just that it makes sense and would work, and that the "it's ambiguous" argument is, uhm, weak at best. It provides feature parity with VB. Don't reject the idea of someone doing it. @Corey-M commented on Thu Apr 07 2016 Feature parity aside, I believe that this is still a valid extension to C#. The fact that it is possible in VB.NET shows that it is possible and CLR-supported, so that's not an impediment. The issues raised here seem to revolve around the way that it is used in a particular example... which seems to be a frivolous objection, since there are certainly other use cases which don't necessarily suffer from the same perceived problems. For instance, I have been working on an extendable wrapper for executing scripts and commands in an embedded PowerShell
This allows me to completely abstract the session state of the PowerShell I have other collections that I would also like to be able to access this way, and will end up coding as above, but would much prefer to do this:
Shorter code, much more self-explanatory, fewer issues with object initialization (no null assignment to the Another issue with the first code block above is that lambda expressions do funny things to garbage collection of the objects they capture. I've had to implement the And finally, there is not way to prevent or track external references to @alrz commented on Thu Apr 07 2016 @Corey-M I believe #6136 can address your use case, something like extension Runspace {
public object this[string key] {
get { return GetVariable(key); }
set { SetVariable(key, value); }
}
} @Corey-M commented on Thu Apr 07 2016 @alrz Nice thought, but in order to use an extension on an object I would need to expose that object, which I do not wish to do in this case. All interactions with the I suppoort #6136 in principle - I think it'd be a nice feature to have - but don't see a simple implementation path. Indexed properties (named indexers) already have been implemented in VB so there are no obvious impediments to C# implementation. This is a comparatively simple feature to add. @alrz commented on Thu Apr 07 2016 To hide implementation you'd better use interfaces (#8127). @Corey-M commented on Thu Apr 07 2016 @alrz I'm sorry but your extensions do not fit my needs, and interfaces do not prevent any of the issues I have raised. A reference to an interface can still be cached external to the class instance, the interface still needs to be assigned a concrete object whose lifecycle requires management, code is rendered less readable and so on. All of these points (and more) would be resolved by indexed properties. @HaloFour commented on Fri Apr 08 2016 I could create a delegate to your indexed property and hold onto it forever, thereby holding a root reference to your Yes, it would be easy to add. It always has been. Yet the team intentionally didn't add it, and they continue to question their utility and discourage their pattern today. They never need a reason to not do something. @Corey-M commented on Sat Apr 09 2016 @HaloFour In the current incarnation yes, you could hold all sorts of references, including a lot of unintentional ones. That's the problem. But how would that work for an indexed property? What would it look like to do so? Referencing the property itself without an index should fail at compile time and with an index it is not going to be returning a delegate (unless that's what is stored in the backing collection). How exactly would you get that delegate reference? How would you get a delegate to a standard property get/set method for that matter? Reflection? If bypassing via Reflection is reason not to have a feature then you might as well rip out
So what you're saying is I should never bother asking or showing support for features that might have come up before because The Team is all-wise, their decisions should never be questioned and no reasons need be given? Wow. Just wow. I thought that maybe if we could explain the utility of the feature, explain how we intend to use it and what benefits we would get from doing so, showed support for the feature and so on that maybe - just maybe - they might decide that the feature was actually worth taking another look at, and maybe one day I might get an improvement that will help me with this mess. Is that not the point of forums like this? @HaloFour commented on Sat Apr 09 2016
You can create a delegate to any method, including property accessors. Simplest C# syntax would be through a closure: Func<string, object> d = index => runner.Variables[index];
Repeating the same questions will generally just result in the same answers. I think it's fine to question those answers, just as I think it's fine to question the questioning of those answers. The Team ultimately owns their language, though. @Corey-M commented on Sat Apr 09 2016 @HaloFour Your lambda doesn't extract a delegate for the property get method, it closes around the parent object ( What I can do though is track those references down in the the code, or in the IL if necessary. Adding my
Have I implied otherwise? If so I apologize. But should I consider their dislike of the idea the final word and not try to show how it could be beneficial? @alrz commented on Sat Apr 09 2016 This is already backlogged so considering all the things that can be done for C# it is probably up for C# 11. @HaloFour commented on Sat Apr 09 2016 @Corey-M You're right, C# offers no direct syntax to obtain a delegate for a property. However, there are several proposals to add that syntax, not to say that any of them will be implemented. But property accessor methods are just normal methods so some languages could offer that syntax, or you could use reflection to obtain a delegate directly: PSRunner runner = new PSRunner();
PropertyInfo variablesProperty = typeof(PSRunner).getProperty("Variables");
MethodInfo getterMethod = variablesProperty.GetGetMethod();
Func<string, object> d = (Func<string, object>)Delegate.CreateDelegate(typeof(Func<string, object>), runner, getterMethod);
No, but you'd need a team member to champion the feature in order to get it implemented, even if you (or someone else) were to actually do the work. If they don't think that it's worth the cost in terms of effort and permanent support then it won't happen. In terms of showing how it could be beneficial, I'm not seeing anything new here. |
Beta Was this translation helpful? Give feedback.
Replies: 127 comments 48 replies
-
Example with proposed indexer syntax:
Example with current indexer syntax:
Caller code (same for current indexer syntax and proposed indexer syntax):
That makes the original indexers much like the proposed new indexers. I still think it's weird that it needs its own interface and class. I hope the examples help. |
Beta Was this translation helpful? Give feedback.
-
I don't understand the ambiguity argument, isn't the answer to do the same thing VB does? Also all the arguments seem to be targeting you as the authors of the code. Doesn't really help if you need to consume 3rd party or interop that use this feature. |
Beta Was this translation helpful? Give feedback.
-
I removed my post with example code that was only complicated, because I did it wrong. |
Beta Was this translation helpful? Give feedback.
-
I always wish we could have this feature And if we have this feature it would be natural to introduce extension property syntax public static Ext
{
public static string SafeString[this object obj]
{
get => obj?.ToString();
}
public static string SafeStringIndex[this object[] array,int i]
{
get => array?.Length > i ? array[i].ToString() : null;
}
}
object[] array;
var name = array[0].SafeString;
var name0 = array.SafeStringIndex[0]; |
Beta Was this translation helpful? Give feedback.
-
I have started my project with C++/CLI (due to including C++ DLLs and other reasons) and currently convert most of the code to C#. NOTICE: Like VB.net, C++/CLI also is a .net language
It's a big pain in the ass to work around that now and find a suitable implementation in C#. |
Beta Was this translation helpful? Give feedback.
-
I'm in the same boat as @TobiasKnauss; I am working on a C++/CLI project in which I'm exposing few properties through indexed properties. I still don't get why C++/CLI, VB.NET, AND F# all support this feature, but C# doesn't. It seems pretty convenient, and getting the equivalent outcome in C#shown in the post by @jjvanzon is needlessly obtuse. |
Beta Was this translation helpful? Give feedback.
-
My assumption is that C# designers wanted to reduce the risk that something looks like a property returning a collection. I'm OK with that -- being able to scan through code is useful, and depending on Intellisense would not be an adequate replacement for that. The only reason I can see for doing this is to improve performance via increased locality and reduced GC pressure, which may be a good reason to revisit this. But again, C# has always placed productivity over performance. |
Beta Was this translation helpful? Give feedback.
-
@scalablecory: if this feature is not about productivity, what else? Indexed properties make programming much easier in certain situations: you don't have to get a copy of a collection, but you can get directly that one element that you're looking for. There's no chance for misinterpretation, compiler and Intellisense will give you all useful info, and if you also choose a good name for it, then nothing can go wrong. |
Beta Was this translation helpful? Give feedback.
-
@TobiasKnauss I'm just repeating myself at this point, but again Intellisense wouldn't be good enough. It is good to be able to read code with your eyeballs and not need to hover over everything to understand what is happening. I also have a hard time thinking of the usefulness for this. I can't think of any instance where I would have used it, and none of the examples given look like good code to me. Perhaps if you can provide some broader real-world examples that would benefit from this -- ideally it'd demonstrate enough "bang" that for the "buck" of reduced readability. |
Beta Was this translation helpful? Give feedback.
-
Excuse the lengthy post. When I realized the proposed indexer syntax does not differ greatly from the current indexer syntax, I rested my case, because I am OK with it in its current form. But given the recent continuation of the discussion, I feel moved to add a few more arguments to the mix. The fact that I had some difficulty wrapping my head around how to implement these multiple indexers, says something. I am a pretty experienced programmer, so this should be easy for me. Yet it was not. You could argue I must be simply be too dumb to find this easy, but I somehow don't think that's it. It's more like... you're thinking 'I want an indexer here. I just want [] to work' and it takes you a while to realize you are going to need to instantiate a totally different object. Your intent and what you have to program have some sort of gap in between. That might be something the C# language designers find important (expressiveness). I think this may actually prevent less experienced programmers from using these things, which would be kind of a shame. Also, if you want these indexers to be part of an interface, that you expect other programmers to implement, it prevents you from saying: 'Here you have an interface. Implement it, and you're good.' Implementors instead now have to implement two interfaces and kind of awkwardly tie them together. Implementors might bump into the same kind of mental leap you have to take to simply 'make [] work here'. What was that principle called? Principle of least astonishment? It kind of applies here. The alternative? Just not use indexers. But that kind of misses the point. This should be about how we would like to use indexers. Not how we would like to... not use indexers. The example I wrote is pretty much a real world example. It is part of a sound synthesizer program that I made, that hooks your MIDI keyboard to a user-configured sound calculation. As for the 'none of it looks like good code' argument. It makes you sound judgy. I actually benefitted from the multiple ways to pass musical parameters to my sound calculation. Sometimes my calculation has one pitch, sometimes it has more, sometimes I need to set all the pitches at once, sometimes a very specific one. Actually the user kinda needs this flexibility. You want to be able to specify a musical parameter name yourself, but the system also needs a standard vocabulary of parameters to know which is which, hence the enum. And to get everything tied together nicely between user, keyboard, sound output and the calculation, my code needs to be able to talk with the calculation in the most easy to read form that I can come up with. I am well aware of the code smells it has, but it turned out to work quite handy this way. (I think that is a pretty compelling argument in software design.) |
Beta Was this translation helpful? Give feedback.
-
There are several arguments that seem to support this feature, and so far the objections seem to be primarily based on "I don't want that in my code" or "it'd be too hard to read" or similar sentiments. For instance:
I have a hard time thinking of a valid use for attributes on generic type parameters, but they exist. So being unable to think of a good use for a feature seems to have zero impact on the discussion. Some of the points raised so far in favour:
And against (with some notes):
|
Beta Was this translation helpful? Give feedback.
-
I'm not sure i understand any of these points:
If i really felt like i needed this in C# i would simply return a struct that exposed such an indexer on it. i.e. something like: class Whatever {
MyIndexer SomeProp => new MyIndexer { _this = this };
private int GetValue(string a, string b) => ...;
public struct MyIndexer {
internal Whatever _this;
public int this[string a, string b] => _this.GetValue(a, b);
}
} Now you have parameterized indexers with no overhead. Indeed, in practice the runtime should be smart enough to elide effectively all of this and know that when you do GC and capture simply do not play into this at all. |
Beta Was this translation helpful? Give feedback.
-
This is the reverse of how we generally think about language features. We don't start with 'x, y and z have it, so C# should'. Instead we have to have a strong reason why such a feature is really needed for the language, and why existing workarounds aren't satisfactory. For example, C# can already consume parameterized properties. So if you have one of these properties in C++/CLI or VB or F#, C# can already use them. So the only question is about production of them. And, right not, it has never been super compelling to do provide language support for this. There isn't high demand in it (i.e. we haven't really had any requests about this from huge APIs like teh BCL for going on 15+ years), and (as shown in my previous post) there are simple ways to accomplish the same goal with existing constructs. Yes, it would make thigns simpler from an authoring perspective to just have this in the language. But only marginally so. From the consumption side it would be the same. Give that htis only really helps the API author, and only a little, and there aren't that many API authors that need it, that's why it's always been a feature that has never really had anyone really wanting to invest their time into it. |
Beta Was this translation helpful? Give feedback.
-
This is not a good argument for having a language feature. Language features come with an enormous cost. The spec for C# is already massive, and every thing added just adds more and more to an already huge and expensive to maintain language. A first thought I always have whne it comes to language feature requests is "is this really valuable enough to make part of the core language?" as well as "can this already be accomplished in a good enough fashion with what has already shipped up to now. If the answers to those are either "no" or "yes" (respectively), then i would not want the feature added to the language. Similarly, I often ask myself "who is this benefitting". If this is something that only benefits, say, the authors of APIs, then that's a lot less awesome than something that benefits all the consumers of said APIs. There is usually around a 1000:1 ratio (or more) of code consumers to API authors. So it can be a little more painful for the latter group if we focus more on the former group. |
Beta Was this translation helpful? Give feedback.
-
Oh?
The above captures an instance of the That's what the reference capture is all about, and why the GC gets involved.
I wasn't arguing for the feature, I was pointing out that "proposed feature
Who exactly benefits from attributes on generic parameters? That's in the standard. I've yet to see it used, but it's there. Personally I can't even think of a scenario where I might want to use such a thing, and I've done some pretty weird stuff with code over the years. Should we all be campaigning to have such a useless feature removed in order to help reduce the complexity of the language? If not, then I consider your objection to have no merit. |
Beta Was this translation helpful? Give feedback.
-
@Thaina no copying should necessary. You can just do things like: struct Matrix4x4
{
private readonly float[] values;
public Matrix4x4(int whatever)
{
values = new float[16];
}
public Matrix4x4Rows Rows => new Matrix4x4Rows(values);
}
struct Matrix4x4Rows
{
private readonly float[] values;
public Matrix4x4Rows(float[] values) => this.values = values;
public Vector4 this[int index] => new Vector4(values, index);
}
struct Vector4
{
private readonly float[] values;
private readonly int rowIndex;
public Vector4(float[] values, int rowIndex)
{
this.values = values;
this.rowIndex = rowIndex;
}
public ref float this[int column] => ref values[rowIndex * 4 + column];
}
class C
{
static void Main(string[] args)
{
var matrix = new Matrix4x4(whatever: 0);
matrix.Rows[1][2] = 3;
}
} |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi This is what You don't understand, While indexed property could be just public partial struct Matrix4x4 : System.IEquatable<System.Numerics.Matrix4x4>
{
public float M11;
public float M12;
public float M13;
public float M14;
public float M21;
public float M22;
public float M23;
public float M24;
public float M31;
public float M32;
public float M33;
public float M34;
public float M41;
public float M42;
public float M43;
public float M44;
public Vector4 Rows[int i]
{
get => switch(i) // just use switch expression for convenience
{
0 => new Vector4(M11,M12,M13,M14);
1 => new Vector4(M21,M22,M23,M24);
2 => new Vector4(M31,M32,M33,M34);
3 => new Vector4(M41,M42,M43,M44);
_ => throw IndexOutOfRange;
}
set => switch(i)
{
0 => (M11,M12,M13,M14) = value; // assume vector can be deconstruct to 4 floats
1 => (M21,M22,M23,M24) = value;
2 => (M31,M32,M33,M34) = value;
3 => (M41,M42,M43,M44) = value;
_ => throw IndexOutOfRange;
}
}
public Vector4 Columns[int i] // same as rows just transposing
} |
Beta Was this translation helpful? Give feedback.
-
Gotcha. Sounds like something ref-fields would help with. you could return then return through a ref struct that points to your values by ref. |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi Yeah but it also mean this is the same level of impossibility in C#. It need some new feature like And so your workaround is not fully replaceable for indexed property |
Beta Was this translation helpful? Give feedback.
-
Yup. But i would champion |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi wouldnt |
Beta Was this translation helpful? Give feedback.
-
The matrix would be a normal struct. The proxy would contain a ref to it. So the proxy couldn't move to the heap. That would be fine given that you couldn't do that either with a parameterized propriety. |
Beta Was this translation helpful? Give feedback.
-
Here's another reason for implementing indexed properties: In the likely most used workaround of creating an helper class, e.g. https://stackoverflow.com/a/17618809, you can't create intellisense comments in suitable way. |
Beta Was this translation helpful? Give feedback.
-
We will see one day C# will implement it. Lets remain hopeful. |
Beta Was this translation helpful? Give feedback.
-
At this point, if you want it done, fork the compiler. I seriously considered it but it would take more momentum than just myself to maintain a fork. |
Beta Was this translation helpful? Give feedback.
-
This is such a useful feature that should be implemented...makes coding faster...easier to debug...more powerful... |
Beta Was this translation helpful? Give feedback.
-
This is such a useful feature that should be implemented...makes coding faster...easier to debug...more powerful... |
Beta Was this translation helpful? Give feedback.
-
I have a couple dozen methods that have this pattern:
These would more naturally be implemented as an indexer. This required an additional class, additions to the constructors to initialize an indexer proxy class for each pair of these methods. Feels like alot of unnecessary scaffolding and instantiation. It feels like a couple functions which fit a get/set pattern well, in the effort to reduce them down to an indexer, ballooned out quite a bit.
|
Beta Was this translation helpful? Give feedback.
-
like this , you can have a type seperate indexer, but the T1 can't be the same with T2, we need a clean way to access both setter and getter:
if there's a named indexer, it would be helpful |
Beta Was this translation helpful? Give feedback.
-
@alrz to provide a 'real' use case where an 'indexed property' is not the same as a property with an indexer (this is 'theorized' from a project I am working on), and yes I know that I can always just use 'Get/Set' methods, but this would be much more useful to me namespace ChangedForAtLeastALittleSecrecy;
public class Tile
{
[NonSerialized] private DirectionArray<Tile> m_Neighbors = new DirectionArray<Tile>();
public Tile Neighbor[Direction direction]
{
get {
return m_Neighbors[direction];
}
set {
if (m_Neighbors[direction] != null)
return;
m_Neighbors[direction] = neighbor;
if (neighbor != null)
neighbor.m_Neighbors[direction.Opposite()] = this; // a property with an indexer would require a 'second' parameter for this part to work
}
}
#endregion
} And yes I do know that this is just ONE use case, but it does demonstrate that an indexed property is not the same, and I also (after about thirty minutes) realized that the posted code doesn't show why not just use an indexer directly, and that is because I actually have more than one stored DirectionArray each of which have 'special' requirements for setting and all would benefit from a named indexer |
Beta Was this translation helpful? Give feedback.
There was no such decision. We literally did not have a design for generics when 1.0 was released. The time from 1.0 to 2.0 was where we actually created and implemented the design.